iOS开发之Realm数据库的简单使用

1.安装

Realm github下载地址

如果使用cocoapods,示例语句如下
platform :ios, '8.0'
target 'Simona_Realm' do
pod 'Realm', '~> 3.0.0-beta.3'
end

2.创建简易模型,继承于RLMObject,存储在数据库的模型都需要继承于RLMObject

  1. Person.h
  2. #import <Realm/Realm.h>
  3. RLM_ARRAY_TYPE(Person);
  4. @interface Person : RLMObject
  5. @property NSString *personId;
  6. @property BOOL status;
  7. @property NSString *sex;
  8. @end
  1. Person.m
  2. #import “Person.h”
  3. @implementation Person
  4. // 设置主键,确保数据唯一性
  5. + (NSString *)primaryKey {
  6. return @”personId”;
  7. }
  8. // 设置属性不为nil
  9. + (NSArray<NSString *> *)requiredProperties {
  10. return @[@”sex”];
  11. }
  12. // 设置忽略属性
  13. + (NSArray<NSString *> *)ignoredProperties {
  14. return @[];
  15. }
  16. // 设置默认值,对于不为null的属性,默认给空
  17. + (NSDictionary *)defaultPropertyValues {
  18. return @{@”sex”:@””};
  19. }
  20. //索引属性,主要用于搜索,根据性别进行搜索
  21. + (NSArray<NSString *> *)indexedProperties {
  22. return @[@”sex”,@”woman”];
  23. }
  24. @end

关于RLMObject
1.Realm忽略了OC的属性特性(如nonatomic, atomic, strong,retain, weak,copy等),所以在声明属性时可不写,,这些特性会一直生效直到被写入数据库。
2.Realm支持以下的类型BOOL, NSInteger, long, double, CGFloat, NSString, NSDate, NSData
3.定义了RLM_ARRAY_TYPE(Person)表示支持RLMArray属性,相当于允许RLMArray<Person>属性的使用,例如:在其他属性里可@property RLMArray<Person *><Person> *personal如此使用,相当于继承关系(本文暂不做赘述)

3.使用

<1.>存储

  1. 模拟存储一万条假数据
  2. RLMRealm *realm = [RLMRealm defaultRealm];
  3. [realm transactionWithBlock:^{
  4. for (int i = 0; i < 10000; i++) {
  5. Person *person = [[Person alloc]init];
  6. person.personId = [NSString stringWithFormat:@“张%d”,i];
  7. person.sex = @“Not scanned”;
  8. person.status = YES;
  9. [realm addObject:person];
  10. }
  11. }];

这里的
[realm transactionWithBlock:^{ }];
等同于
[realm beginWriteTransaction];
[realm commitWriteTransaction];
每次打开关闭数据库时都应执行此操作

<2.>删除

  1. RLMRealm *realm = [RLMRealm defaultRealm];
  2. [realm beginWriteTransaction];
  3. [realm deleteAllObjects];
  4. [realm commitWriteTransaction];

<3.>查询

  1. 1.根据谓词查询
  2. // RLMResults相当于查询到的数组
  3. NSPredicate *pred = [NSPredicate predicateWithFormat:@”status = %ld”, NO];
  4. RLMResults *result = [Person objectsWithPredicate:pred];
  5. Person *per = result[indexPath.row];
  6. 2.根据条件查询
  7. RLMResults *result = [Person objectsWhere:@”status = ‘NO’ AND personId BEGINSWITH ‘张1′”];
  8. Person *per = result[indexPath.row];
  9. 3.查询所有
  10. RLMResults *persons = [Person allObjects];
  11. Person *per = persons[indexPath.row];

<4.>更新

  1. 1.更新数据里某一属性值
  2. NSPredicate *pred = [NSPredicate predicateWithFormat:@“personId = %@”, strScan];
  3. RLMResults *result = [Person objectsWithPredicate:pred];
  4. if (result.count != 0) {
  5. RLMRealm *r = [RLMRealm defaultRealm];
  6. [r beginWriteTransaction];
  7. //直接修改
  8. Person *person = result[0];
  9. person.sex = @“Has been scanned”;
  10. person.status = NO;
  11. [r commitWriteTransaction];
  12. }
  13. 2.创建一个新值并更新数据库,createOrUpdateInRealm:通过主键来更新插入
  14. NSPredicate *pred = [NSPredicate predicateWithFormat:@“personId = %@”, strScan];
  15. RLMResults *result = [Person objectsWithPredicate:pred];
  16. if (result.count != 0) {
  17. Person *person = [[Person alloc]init];
  18. person.personId = @“Simona1”;
  19. person.sex = @“woman”;
  20. person.status = NO;
  21. RLMRealm *r = [RLMRealm defaultRealm];
  22. [r beginWriteTransaction];
  23. [Person createOrUpdateInRealm:r withValue:person];
  24. [r commitWriteTransaction];

 

iOS字符串匹配算法2个字符串相似度(Levenshtein:莱文斯坦距离)

Levenshtein的经典算法,参考http://en.wikipedia.org/wiki/Levenshtein_distance的伪代码实现的,同时参考了一些C++的实现,求字符串相似度。

下面求出结果是0.0~100.0,   表示为0%~100%。

  1. static inline int min(int a, int b) {
  2. return a < b ? a : b;
  3. }
  4. +(float)likePercentByCompareOriginText:(NSString *)originText targetText:(NSString *)targetText{
  5. //length
  6. int n = (int)originText.length;
  7. int m = (int)targetText.length;
  8. if (n == 0 || m == 0) {
  9. return 0.0;
  10. }
  11. //Construct a matrix, need C99 support
  12. int N = n+1;
  13. int **matrix;
  14. matrix = (int **)malloc(sizeof(int *)*N);
  15. int M = m+1;
  16. for (int i = 0; i < N; i++) {
  17. matrix[i] = (int *)malloc(sizeof(int)*M);
  18. }
  19. for (int i = 0; i<N; i++) {
  20. for (int j=0; j<M; j++) {
  21. matrix[i][j]=0;
  22. }
  23. }
  24. for(int i=1; i<=n; i++) {
  25. matrix[i][0]=i;
  26. }
  27. for(int i=1; i<=m; i++) {
  28. matrix[0][i]=i;
  29. }
  30. for(int i=1;i<=n;i++)
  31. {
  32. unichar si = [originText characterAtIndex:i-1];
  33. for(int j=1;j<=m;j++)
  34. {
  35. unichar dj = [targetText characterAtIndex:j-1];
  36. int cost;
  37. if(si==dj){
  38. cost=0;
  39. }
  40. else{
  41. cost=1;
  42. }
  43. const int above = matrix[i-1][j]+1;
  44. const int left = matrix[i][j-1]+1;
  45. const int diag = matrix[i-1][j-1]+cost;
  46. matrix[i][j] = min(above, min(left,diag));
  47. }
  48. }
  49. return 100.0100.0*matrix[n][m]/MAX(m,n);
  50. }

 

iOS开发 — IQKeyboardManager使用

IQKeyboardManager的使用非常简单,只需要在项目使用到的地方之前给IQKeyboardManager的单例设置一些属性就能轻松实现。下面来介绍一下这些属性:

建议在AppDelegate的

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullableNSDictionary *)launchOptions{}方法中设置属性。

例如:属性设置(配置信息)

 

IQKeyboardManager *manager = [IQKeyboardManagersharedManager];
manager.enable = YES; // 控制整个功能是否启用。
manager.shouldResignOnTouchOutside =YES; // 控制点击背景是否收起键盘
manager.shouldToolbarUsesTextFieldTintColor =YES; // 控制键盘上的工具条文字颜色是否用户自定义
manager.enableAutoToolbar =YES; // 控制是否显示键盘上的工具条
manager.toolbarManageBehaviour =IQAutoToolbarByTag; // *新版的设置键盘的returnKey的关键字 ,可以点击键盘上的next键,自动跳转到下一个输入框,*后一个输入框点击完成,自动收起键盘。

在需要使用的界面设置

 

IQKeyboardReturnKeyHandler *retuenKeyHandler =        [[IQKeyboardReturnKeyHandleralloc]initWithViewController:self];
retuenKeyHandler.lastTextFieldReturnKeyType =UIReturnKeyDone; // 设置*后一个输入框的ReturnKey关键字
这样设置后就能实现输入框随着键盘的弹出自动上下浮动,点击背景收起键盘了,不用再自己实现,非常方便。

*新版的只需要在这个方法中

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullableNSDictionary *)launchOptions{
// 设置键盘监听管理
[[IQKeyboardManager sharedManager] setToolbarManageBehaviour:IQAutoToolbarByPosition];

就可以实现。

如果因为不知名的原因需要在某个页面禁止自动键盘处理事件相应,也很简单。

– (void) viewWillAppear: (BOOL)animated {
//打开键盘事件相应
[IQKeyboardManager sharedManager].enable = NO;
}
– (void) viewWillDisappear: (BOOL)animated {
//关闭键盘事件相应
[IQKeyboardManager sharedManager].enable = YES;

iPhone屏幕尺寸(包含7p)

px与pt区别

字体大小的设置单位,常用的有2种:px、pt。这两个有什么区别呢?

先搞清基本概念:

  • px就是表示pixel,像素,是屏幕上显示数据的*基本的点;
  • pt就是point,是印刷行业常用单位,等于1/72英寸。

px全称为pixel,是一个点,它不是自然界的长度单位,谁能说出一个“点”有多长多大么?可以画的很小,也可以很大。如果点很小,那画面就清晰,我们称它为“分辨率高”,反之,就是“分辨率低”。所以,“点”的大小是会“变”的,也称为“相对长度”。

pt全称为point,但中文不叫“点”,查金山词霸可以看到,确切的说法是一个专用的印刷单位“磅”,大小为1/72英寸。所以它是一个自然界标准的长度单位,也称为“*对长度”。

因此就有这样的说法:

  • pixel是相对大小,
  • point是*对大小。

iPhone各种屏幕分辨率

设备 屏幕尺寸 分辨率(pt) Reader 分辨率(px) 渲染后 PPI
iPhone 3GS 3.5吋 320×480 @1x 320×480 163
iPhone 4/4s 3.5吋 320×480 @2x 640×960 330
iPhone 5/5s/5c 4.0吋 320×568 @2x 640×1136 326
iPhone 6 4.7吋 375×667 @2x 750×1334 326
iPhone 6Plus 5.5吋 414×736 @3x 1242×2208 1080×1920 401
iPhone 6s 4.7吋 375×667 @2x 750×1334 326
iPhone 6sPlus 5.5吋 414×736 @3x 1242×2208 1080×1920 401
iPhone 7 4.7吋 375×667 @2x 750×1334 326
iPhone 7Plus 5.5吋 414×736 @3x 1242×2208 1080×1920 401

%title插图%num

%title插图%num

iOS 截屏指定区域

指定截屏代码实现

全屏截图效果

%title插图%num

全屏截图效果

指定区域截屏效果

%title插图%num

指定区域截屏效果

这里先上代码,代码后面有相关方法的解释
*种方法
代码下载

  1. /**
  2. 创建一个基于位图的上下文(context),并将其设置为当前上下文(context)
  3. @param size 参数size为新创建的位图上下文的大小。它同时是由UIGraphicsGetImageFromCurrentImageContext函数返回的图形大小
  4. @param opaque 透明开关,如果图形完全不用透明,设置为YES以优化位图的存储,我们得到的图片背景将会是黑色,使用NO,表示透明,图片背景色正常
  5. @param scale 缩放因子 iPhone 4是2.0,其他是1.0。虽然这里可以用[UIScreen mainScreen].scale来获取,但实际上设为0后,系统就会自动设置正确的比例了
  6. */
  7. UIGraphicsBeginImageContextWithOptions([[UIScreen mainScreen] bounds].size, YES, 0.0);
  8. UIView * view = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
  9. //把控制器View的内容绘制到上下文当中.
  10. //layer是不能够直接绘制的.要用渲染的方法才能够让它绘制到上下文当中。UIGraphicsGetCurrentContext()
  11. [view.layer renderInContext:UIGraphicsGetCurrentContext()];
  12. //从上下文当中生成一张图片
  13. UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  14. //关闭上下文.
  15. UIGraphicsEndImageContext();
  16. /*
  17. //image:要把图片转成二进制流,compressionQuality:可压缩质量.
  18. NSData*data =UIImagePNGRepresentation(viewImage);
  19. */
  20. //上面我们获得了一个全屏的截图,下边的方法是对这个图片进行裁剪。
  21. CGImageRef imageRef =viewImage.CGImage;
  22. //这里要特别注意,这里的宽度 CGImageGetWidth(imageRef) 是图片的像素宽(高度同解),所以计算截图区域时需要按比例来;
  23. //这里举的例子是宽高屏幕1/2位置在中心
  24. CGRect rect = CGRectMake(CGImageGetWidth(imageRef)/4, CGImageGetHeight(imageRef)/2CGImageGetWidth(imageRef)/2, CGImageGetWidth(imageRef)/2, CGImageGetWidth(imageRef)/2);//这里可以设置想要截图的区域
  25. CGImageRef imageRefRect =CGImageCreateWithImageInRect(imageRef, rect);
  26. UIImage *sendImage =[[UIImage alloc] initWithCGImage:imageRefRect];

第二种方法
代码下载

  1. //设置边框宽度
  2. CGFloat borderWH = 5;
  3. //图片宽度(像素)
  4. CGFloat imageWH = self.baseImageView.image.size.width;
  5. NSLog(@”=========%f”,imageWH);
  6. //开启一个位图上下文(W = imageWH + 2 * border H = imageWH + 2 * border)
  7. CGSize size =CGSizeMake(imageWH + 2*borderWH, imageWH + 2*borderWH);
  8. UIGraphicsBeginImageContextWithOptions(size,NO,0);
  9. //绘制一个圆形的形状.
  10. UIBezierPath*path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0, size.width, size.height)];
  11. //边框颜色
  12. UIColor *color = [UIColor redColor];
  13. [color set];
  14. [path fill];
  15. //设置一个小圆,并设置成裁剪区域
  16. path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(borderWH, borderWH, imageWH, imageWH)];
  17. //把路径设置成裁剪区域
  18. [path addClip];
  19. //把图片绘制到上下文当中.
  20. [self.baseImageView.image drawAtPoint:CGPointMake(borderWH, borderWH)];
  21. //从上下文当中生成一张图片
  22. UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();
  23. //关闭上下文.
  24. UIGraphicsEndImageContext();
  25. self.bottonImageView.image = newImage;

相关知识详解

UIGraphicsBeginImageContext创建一个基于位图的上下文(context),并将其设置为当前上下文(context)。方法声明如下:

void UIGraphicsBeginImageContext(CGSize size);

参数size为新创建的位图上下文的大小。它同时是UIGraphicsGetImageFromCurrentImageContext函数返回的图形大小。
该函数的功能同UIGraphicsBeginImageContextWithOptions的功能相同,相当与UIGraphicsBeginImageContextWithOptionsopaque参数为NO,scale因子为1.0。

UIGraphicsBeginImageContextWithOptions函数原型为:

void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);

size——同UIGraphicsBeginImageContext
opaque—透明开关,如果图形完全不用透明,设置为YES以优化位图的存储。
scale—–缩放因子 iPhone 4是2.0,其他是1.0。虽然这里可以用[UIScreen mainScreen].scale来获取,但实际上设为0后,系统就会自动设置正确的比例了。


其他的截屏方法

第二种
这是在比较常见的截图方法,不过不支持Retina屏幕。

  1. UIGraphicsBeginImageContext(self.view.frame.size);
  2. [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
  3. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  4. UIGraphicsEndImageContext();
  5. return image;

第三种
从iPhone 4、iPod Touch 4开始,Apple逐渐采用Retina屏幕,于是在iOS 4的SDK中我们有了,上面的截图方法也自然变成了这样。

  1. UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0.0);
  2. [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
  3. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  4. UIGraphicsEndImageContext();
  5. return image;

第四种
或许你会说有时Hook的是一个按钮的方法,用第三个方法的话,根本找不到view来传值,不过还好,iOS 7又提供了一些UIScreen的API。

  1. UIView * view = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
  2. UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
  3. [view.layer renderInContext:UIGraphicsGetCurrentContext()];
  4. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  5. UIGraphicsEndImageContext();
  6. return image;

 

Android新手,内存泄漏问题

问题描述

  • A = 主页面 fragment
  • B = 输入内容页 fragment
  • C = 检索结果页 fragment 我在 A 中进行 replace 跳转到一个 B,完成输入后 replace 跳转到 C,此时使用 home 键进入后台运行,发生内存泄漏问题

报错情况

D/HomePageFragment: BaseFragment-->onPause()
D/DiscoveryFragment: BaseFragment-->onPause()
D/TvItemsFragment: BaseFragment-->onPause()
D/MainActivity: BaseActivity-->onPause()
D/LeakCanary: Scheduling check for retained objects in 5000ms because app became invisible
D/HomePageFragment: BaseFragment-->onStop()
D/DiscoveryFragment: BaseFragment-->onStop()
D/CommendFragment: BaseFragment-->onStop()
D/TvItemsFragment: BaseFragment-->onStop()
D/MainActivity: BaseActivity-->onStop()
D/MainActivity: BaseActivity-->onSaveInstanceState()

D/LeakCanary: ====================================
    HEAP ANALYSIS RESULT
    ====================================
    1 APPLICATION LEAKS
    
    References underlined with "~~~" are likely causes.
    Learn more at https://squ.re/leaks.
    
    53078 bytes retained by leaking objects
    Signature: b01ba777d9ce636d68e71237f54fafe95ee827f8
    ┬───
    │ GC Root: System class
    │
    ├─ android.view.inputmethod.InputMethodManager class
    │    Leaking: NO (InputMethodManager↓ is not leaking and a class is never leaking)
    │    ↓ static InputMethodManager.sInstance
    ├─ android.view.inputmethod.InputMethodManager instance
    │    Leaking: NO (ViewRootImpl↓ is not leaking and InputMethodManager is a singleton)
    │    ↓ InputMethodManager.mCurRootView
    ├─ android.view.ViewRootImpl instance
    │    Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking and ViewRootImpl#mView is not null)
    │    ↓ ViewRootImpl.mImeFocusController
    ├─ android.view.ImeFocusController instance
    │    Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking)
    │    ↓ ImeFocusController.mNextServedView
    ├─ androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl instance
    │    Leaking: NO (SearchFragment↓ is not leaking and View attached)
    │    mContext instance of com.moviemore.android.ui.MainActivity with mDestroyed = false
    │    View.parent androidx.viewpager2.widget.ViewPager2 attached as well
    │    View#mParent is set
    │    View#mAttachInfo is not null (view attached)
    │    View.mID = R.id.null
    │    View.mWindowAttachCount = 1
    │    ↓ ViewPager2$RecyclerViewImpl.mAdapter
    ├─ com.moviemore.android.ui.common.ui.BaseViewPagerFragment$VpAdapter instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ BaseViewPagerFragment$VpAdapter.mFragmentManager
    ├─ androidx.fragment.app.FragmentManagerImpl instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ FragmentManagerImpl.mFragmentStore
    ├─ androidx.fragment.app.FragmentStore instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ FragmentStore.mActive
    ├─ java.util.HashMap instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ HashMap.table
    ├─ java.util.HashMap$Node[] array
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ HashMap$Node[].[6]
    ├─ java.util.HashMap$Node instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ HashMap$Node.value
    ├─ androidx.fragment.app.FragmentStateManager instance
    │    Leaking: NO (SearchFragment↓ is not leaking)
    │    ↓ FragmentStateManager.mFragment
    ├─ com.moviemore.android.ui.search.SearchFragment instance
    │    Leaking: NO (Fragment#mFragmentManager is not null)
    │    ↓ SearchFragment.rootView
    │                     ~~~~~~~~
    ╰→ android.widget.LinearLayout instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.moviemore.android.ui.search.SearchFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
    ​     key = 888671cb-ecda-4776-b4dd-b4f2350acb32
    ​     watchDurationMillis = 7210
    ​     retainedDurationMillis = 2208
    ​     mContext instance of com.moviemore.android.ui.MainActivity with mDestroyed = false
    ​     View#mParent is null
    ​     View#mAttachInfo is null (view detached)
    ​     View.mWindowAttachCount = 1
    ====================================
    0 LIBRARY LEAKS

3 条回复    2021-07-29 16:08:15 +08:00

JellyBeanX
    1

JellyBeanX   2 天前

要么,在 A 的 onDestroyView() 中销毁 LinearLayout 的实例,要么把 replace 换成 show & hide
JellyBeanX
    2

JellyBeanX   2 天前

@JellyBeanX 说错了,是销毁持有 linearLayout 实例的对象
ukyoo
    3

ukyoo   59 分钟前

onDestroyView()后要手动把成员变量的 View 置空, 因为下一次 onCreateView()还会重新 infalte 一次布局, 这个成员变量就没什么用了, 视为泄露

有没有非 root 的方式在安卓本机填充数据

想用数据填充安卓手机防止恢复,但是小米 10 的 usb2.0 我真的是受够了,有没有什么方法能在手机上直接填充。

10 条回复    2021-07-26 11:32:41 +08:00

hs0000t
    1

hs0000t   3 天前

SD 卡
Kiriya
    2

Kiriya   3 天前

手机开个 FTP 服务器,然后用电脑往里拷数据
learningman
    3

learningman   3 天前 via Android

装个 termux 然后 dd
anguiao
    4

anguiao   3 天前 via Android

如果你是为了出二手清空数据的话,现在都是全盘加密的,把 ID 退了重新刷机就好了,没必要弄这些了。
jim9606
    5

jim9606   3 天前

原厂系统都是有加密的,在原厂 recovery 做一次工厂级恢复出厂设置就行。
RikkaW
    6

RikkaW   3 天前 via Android

? 你们 Android 的给用户的存储都是虚拟了一层又一层的
填充一下下好像也起不到该有的作用 吧(
singerll
    7

singerll   3 天前 via Android

@RikkaW 啥时候虚拟过?我记得就是隐藏了一部分路径而已。
des
    8

des   3 天前 via iPhone

好耶 Rikka 出现了(

先恢复出厂,再多弄几个文件填满就行了

MoeMoesakura
    9

MoeMoesakura   3 天前 via Android

终端 dd if=/dev/random of=/sdcard/1 bs=1m
(话说直接出厂不方便吗
sky96111
    10

sky96111   3 天前

没必要,开个全盘加密然后格式化 data 就够了

Android 开发笔记本选择

 

想买一台笔记本做 Android 开发,之前一直习惯在 Mac 下面开发,所以更倾向于 Mac,问题来了。

M1 架构的 Mac,听说模拟器支持的稀烂,而我会经常使用模拟器。 Macbook Pro 16 寸的,CPU 还是 9 代的,现在入,总觉得差点意思。

有人建议买个高配的 Intel CPU 的 Windows 笔记本,弄成黑苹果,感觉太麻烦了。

所以想整个舒服点的笔记本做开发,现在入 Macbook Pro 16 寸是否合适,听有传言说 9 月份就要发布新的 16 寸的,也不知道是否有 Intel CPU 版本,相当的头疼。

各位彦祖们给个意见吧,多谢

20 条回复    2021-07-27 15:55:29 +08:00

kop1989
    1

kop1989   6 天前   ❤️ 1

1 、有 M1 支持的 Android Studio 和 AVG 。
2 、目前蹩脚的是 AVG 很难安装市面的第三方 APK,因为第三方多数都是 armeabi-v7a 。

所以如果你没有在模拟器上装第三方 apk 的打算,那么目前 M1 是可以胜任的。

lz 剩下的决策顾虑就跟 Android 开发环境没什么关系了。

jinhan13789991
    2

jinhan13789991   6 天前

买 Macbook Pro,出了新版再换
skye
    3

skye   5 天前

mac pro 16 的 intel 版本,m1 搞 android 开发会让你怀疑人生
ntop
    4

ntop   5 天前

我觉得就买 M1 就好,问题都是可以解决的。但是旧款的本本不能变成新款,买新款早享受。
sankemao
    5

sankemao   5 天前

不知道 win11 自带的 android 怎么样
Ackvincent
    6

Ackvincent   5 天前

win 的笔记本装个 Ubuntu 不也挺香的?
个人推荐 dell 的 xps 系列或者 Thinkpad 的 x 系列。
我自己用的是一台 i7-10710U+32G+1T ( SSD )+2T (机械), 系统是 Kali,日常使用爽歪歪。
eminemcola
    7

eminemcola   5 天前

如果一定要买 Mac 的话还是等等看 16 寸 Intel rmbp 会不会更新吧。现目前的 M1 Mac 还没有达到可以在工作环境中完成 Android 开发的程度。
可以参考这篇文章:
https://medium.com/mobile-app-development-publication/apple-m1-is-just-not-ready-for-mobile-development-yet-95735f84d8db
zjsxwc
    8

zjsxwc   5 天前 via Android

买苹果的钱还不如买顶配国产笔记本装 Linux,用 Linux 开发安卓,

话说有了蓝湖后,不再需要用 ps 等软件来看 psd 稿子像素大小,有浏览器就行

对于安卓开发者来说,现在 Linux 完全够用,*少数情况软件不够就 win 虚拟机凑合用。

gouki
    9

gouki   5 天前

@eminemcola #7 除了不太能用模拟器。真机其实不影响
gouki
    10

gouki   5 天前

@zjsxwc #8 这个倒确实。

quella
    11

quella   4 天前

我现在用 m1 模拟器还可以吧,挺流畅。
zpxshl
    12

zpxshl   4 天前 via Android

linux 开发 android 和 mac 开发体验还是不一样的。
有时间且乐于折腾的话,当我没说
20015jjw
    13

20015jjw   4 天前 via Android

看你项目大小
反正我的项目 16 寸顶中顶挺卡
还是得 mac pro 才行
aladdinding
    14

aladdinding   4 天前

开发的话别买 m1 的
2bab
    15

2bab   4 天前

@eminemcola 其实这篇文章下面的评论都是觉得自己用在生产环境没有问题的…Medium 把评论隐藏起来有时候不太友好…我在自己的 M1 Mac Mini 做 Android side project 也觉得不错了~
reanfly
    16

reanfly   3 天前

我也是 android 开发,苹果等等吧,m1 内存 16g 真有点勉强。等不到 m2 了。我的 15 年 mbp 实在是 hold 不住。目前暂时买了 amd 5900hx 笔记本,快到是挺快,win 系统开发还是没有 mac os 舒服呀。
juncat
    17

juncat   3 天前

M1 开发 Android 的话,有的库可能会找不到 osx-aarch_64 的依赖包,一般新版本会更新支持,但是如果是公司项目不方便升级的话,就很难受,例如 [protocolbuffers]( https://github.com/protocolbuffers/protobuf/pull/8557), [AndResGuard]( https://github.com/shwenzhang/AndResGuard/issues/444)。
longforus
    18

longforus   3 天前   ❤️ 1

@Ackvincent mac 我也有,黑苹果也用过,搞到*后还是觉得
win10+wsl 方便好用,不但日常开发,逆向也行,各种工具都 OK,我支持
dingwen07
    19

dingwen07   3 天前 via iPhone

Linux Desktop
Kmmoonlight
    20

Kmmoonlight   2 天前

Android 开发用真机

安卓 11 应用卡死问题

请教大家一个问题,手机刷抖音一段时间应用就卡死,重新打开又正常了,可以切换回桌面,其他正常。我用 adb logcat 看了对应时间点的日志,图像缓冲内存不足?有没有给分析一下是什么原因导致。先谢谢了!

[ 07-24 19:42:00.985 17287:17322 E/AHardwareBuffer ]
GraphicBuffer(w=1, h=1, lc=1) failed (Out of memory), handle=0x0

[ 07-24 19:42:00.985 17287:17322 W/OpenGLRenderer ]
Failed to allocate scratch buffer, error=-12

[ 07-24 19:42:00.985 17287:17322 E/OpenGLRenderer ]
dequeueBuffer failed: Function not implemented (-38)

[ 07-24 19:42:00.985 17287:17322 E/OpenGLRenderer ]
VulkanSurface::dequeueNativeBuffer called with an invalid surface!

[ 07-24 19:42:00.988 17287:17322 E/Parcel   ]
fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 1, fds[i] is -1, fd_count is 2, error: Too many open files

[ 07-24 19:42:00.988 17287:17322 E/Surface  ]
dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: -22

[ 07-24 19:42:00.989 17287:17322 W/OpenGLRenderer ]
dequeueBuffer failed, error = -22; switching to fallback

[ 07-24 19:42:00.989 17287:17322 E/qdgralloc ]
importBuffer: Unable to clone handle

[ 07-24 19:42:00.989 17287:17322 E/GraphicBufferAllocator ]
Failed to allocate (1 x 1) layerCount 1 format 1 usage 10000b00: 5

[ 07-24 19:42:00.996 17287:17322 D/GraphicBufferAllocator ]
GraphicBufferAllocator buffers:
0x98589a90: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x98787190: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x9878ec90: 9766.00 KiB | 1156 (1216) x 2056 |    1 |        2 | 0x10000100 | SurfaceTexture-0-17287-132
0x98791d10: 9766.00 KiB | 1156 (1216) x 2056 |    1 |        2 | 0x10000100 | SurfaceTexture-0-17287-131
0x98796290: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x99825110: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x99831290: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x99832d90: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0x9983ab10: unknown     |  720 ( 768) x 1280 |    1 | 7FA30C06 | 0x20002900 | SurfaceTexture-1-17287-24
0xa3730610: unknown     |  720 ( 768) x 1280 |    1 |

6 条回复    2021-07-26 21:32:19 +08:00

superhxl
    1

superhxl   4 天前 via Android

技术不懂,个人感觉是 11 的问题! 1+8,氧系统,升级系统后经常卡死,升级*新版本也无济于事!*近开始重新拾起 brevent,限制后台才好很多!
Rsplwe
    2

Rsplwe   4 天前

skiavk 的 Bug,时间长了会导致媒体密集型的 fd 泄漏,可以尝试修改 build.prop 的 debug.hwui.renderer 的值为 skiagl
Rsplwe
    3

Rsplwe   4 天前

*媒体密集型的应用

*使用 Vulkan 后端进行 Skia UI 渲染会导致泄漏

janus77
    4

janus77   4 天前

你这截图也看不出啥来
有这空不如 APP 里直接反馈给开发人员 说不定下个版本就修复了
jjpprrrr
    5

jjpprrrr   3 天前

Skia vulkan 的问题,去 build.prop 里把 debug.hwui.renderer 那一条删掉
twoyuan
    6

twoyuan   2 天前

@superhxl #1 我是一加 8T 氧,也是日常无规律卡死,经常遇到系统界面 ANR

安卓开发小技巧,教你们如何用文本 XML 动态生成界面

*步,运行时动态下载 Layout XML 文件,用 binxml 之类的工具函数,转换成 Byte[]类型的二进制 XML (模仿 AndroidStudio 的资源编译流程)

第二步,把每个按钮的资源 ID 包名赋值一下,因为动态载入 Layout 的 JAVA 函数,会自动去 resources.arsc 资源列表查找对应的 ID,而按钮是动态生成的,根本就没有 ID,需要随便分配一个,只要当前 View 内不重复即可。

第三步,用 LayoutInflater.inflate 和老外写的 XmlBlock 内部资源解析函数,正式加载二进制 XML 界面,转换成 View 组件。( https://github.com/liudongmiao/preference-fragment-compat/blob/master/src/me/piebridge/android/preference/PreferenceFragment.java#L235 )

第四步,用 findViewById 查找按钮的 view, 用 setOnClickListener 设置响应事件,setContentView 显示出来,搞定收工。

24 条回复    2021-07-28 09:34:42 +08:00

JellyBeanX
    1

JellyBeanX   8 天前

有具体的应用场景吗
3dwelcome
    2

3dwelcome   8 天前

至于写个 UI,为什么不用 AndroidStudio 直接写,而要绕那么大一个圈子。

是因为现在主流开发模式,都是同一套代码去适配各种平台,不可能为了安卓单独写一整套界面和逻辑,而是添加中间转换层。

比如把跨平台的<div><span>Hello World</span></div>,转换成安卓的<TextView>和<Button>,基于文本 XML 相互转换,要比代码之间的转换方便很多。

3dwelcome
    3

3dwelcome   8 天前

@JellyBeanX “有具体的应用场景吗”

用纯代码创建界面太抽象,而用 AndroidStudio 界面编辑器又定的太死,每次都要编译后才能使用。

这方法也算取个中间值,兼顾了 XML 可阅读性和运行期生成界面。

john6lq
    4

john6lq   8 天前 via iPhone

直接用 webview 不是更好?你这玩意前端不会写,移动端不想写,出问题也不知道找谁。
des
    5

des   8 天前 via iPhone

@john6lq 确实,webview 多好用
3dwelcome
    6

3dwelcome   8 天前

@john6lq 用 webview 也太简单粗暴了,很多悬浮层效果不太好弄。同事说,你们开发出来的安卓软件,界面怎么像 PC 软件,套皮 Webview 还是差那么一点感觉。

前端不用写原始 XML,只要写 HTML 就行了。工具主要用途还是中间层适配,自动转换 HTML->XML,尽可能把 Android 的 XML 界面格式黑盒化。

稳定性也不用担心,解析 XML 生成 UI 那套核心代码,还是调用安卓自己内部的。外围只是模拟资源预编译流程,负责喂安卓 XML 数据,不太容易出问题。

yukiww233
    7

yukiww233   8 天前

槽点太多, 首先难点就是 html->xml 转换过程中布局层级 /相对约束; 要是真有完美转换的技术, 那直接拿对应的设计稿来生成不同平台的布局文件不是更好?
另外 ui 用 html->xml,然后操作 ui 的代码还是用原生 java/kotlin 写? 反正我是不知道怎么操作一个代码自动生成的黑箱 xml, 比如隐藏掉一个 view 之后其他控件会跑到什么位置

另外, 楼主之前没用过 weex/rn 之类的技术么

pipilu
    8

pipilu   8 天前

不错,在现在的 V2 算是硬核了,就是反射调用系统接口,兼容性不一定行吧
fredli
    9

fredli   8 天前

不重要了,jetpack compose 来了
whyrookie
    10

whyrookie   8 天前

Jetpack compose 起来,xml 都不要了

3dwelcome
    11

3dwelcome   8 天前

@yukiww233 就相对简单的 UI 约束而言(对父级的上下左右约束),问题不大的,以前我还专门开过帖子,讨论过 UI 约束布局问题 /t/779153

你说控制响应的代码,我是全部反射到别的语言来处理。安卓可以通过 webview 调用 JS,也可以调用 C++,这两个语言*适合写跨平台的业务逻辑,网上现成的 runtime 库也不少。

我其实没搞懂,官方为什么不提供文本 XML 生成界面的函数,又不是每个项目界面都能写死在 apk 里的。

wjploop
    12

wjploop   8 天前

动态加载页面的同时,逻辑代码不需要动态调整了吗?这方面的需求可以用插件化实现吧。
gtanyin
    13

gtanyin   8 天前

HTML->XML ? React Native 表示,写好了给我抄一下!
araraloren
    14

araraloren   8 天前

XML 有啥可读性??
ParfoisMeng
    15

ParfoisMeng   7 天前

喵喵喵???
zhanlanhuizhang
    16

zhanlanhuizhang   7 天前

https://github.com/Tencent/RapidView 腾讯这个
zhanlanhuizhang
    17

zhanlanhuizhang   7 天前

还有一个用 json,生成界面的。但那个需要预先定制。
3dwelcome
    18

3dwelcome   7 天前

@zhanlanhuizhang 这项目怎么感觉有点不靠谱,现在 android api 发展那么快,连个弹性布局都不支持,没法用啊。

而且 RapidView 写法比较土,既然想用 lua 封装一层,那就不要手写去 Wrap API,直接遍历 java 对象属性和函数多好。

cjh1095358798
    19

cjh1095358798   7 天前

没什么大用,android 都要凉了,整这些有毛用
3dwelcome
    20

3dwelcome   7 天前

@cjh1095358798 对程序员不凉啊,手机市场安卓占比那么多的。

日常总有随手开发一个给自己用小工具的需求。以前是电脑上发布,现在发布就是手机和平板。

而安卓不就是国内主流手机平台嘛。

gam2046
    21

gam2046   7 天前

实际应用场景太局限了,相比之下,WebView 是成本更低的选择。
zhanlanhuizhang
    22

zhanlanhuizhang   5 天前   ❤️ 1

现在走这条路的*好的应该是美团吧: https://tech.meituan.com/2019/08/15/mtflexbox-automation-buried-point-exploration.html https://tech.meituan.com/2019/09/19/litho-practice-in-dynamic-program-mtflexbox.html
就是我昨天说过的用通过下发 json 。里面包含三端统一的 xml 文件。但是现在美团都转向 react,和 Flutter 。主要原因就是:截取美团自我评价( MTFlexbox 基本上支持 Native 上常用的基础控件的展示,对有 UI 定制化的需求支持度很高。但 MTFlexbox 的 XML 布局需要在运行前编写完成,只支持简单的三元表达式,逻辑能力有限。因此,MTFlexbox 特别适合布局样式复杂、变动频繁但交互简单的业务场景。例如美团 App 首页、搜索结果页等。这些业务场景都具备以下两个特点:

面向多业务方:各业务方有自己的个性化丰富样式,且不同时期可能需要不同的样式。

交互简单:点击跳转完成流量输送的简单交互。)

hyb1996
    23

hyb1996   3 天前 via Android

直接 aapt 编译后下发给客户端,几行代码拦截下 resources,就直接能和原生的 xml 一样用了
F1ReKing
    24

F1ReKing   1 天前

compose 来了