android项目中常见问题

1、textSize使用sp,同时在BaseActivity中设置sp的缩放比例,使其不会收系统影响而变化大小。
在BaseActivity中如下设置:

    @Override
    public Resources getResources() {
        Resources res = super.getResources();
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
            Configuration configuration = res.getConfiguration();
            configuration.fontScale = 1f;
            res.updateConfiguration(configuration, res.getDisplayMetrics());
        }
        return res;
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
            Resources res = newBase.getResources();
            Configuration configuration = res.getConfiguration();
            configuration.fontScale = 1f;
            Context newContext = newBase.createConfigurationContext(configuration);
            super.attachBaseContext(newContext);
        } else {
            super.attachBaseContext(newBase);
        }
    }

2、使用json用作网络数据传输时,应该使用String字段取代int字段。

3、按照现在正常密度比(系统的densityDPI根据分辨率和屏幕尺寸为正常的120、160、240、320、480、640时)9:16的安卓机其尺寸为(360dp*540dp)。UI有时会根据iPhone机型使用750px*1334px作图,而按照1dp=2px来算,其结果为(375dp*667dp)。这样放置控件,宽度上会少15dp,高度上会少127dp,如果UI不做图的话,可以根据美观自行处理(通常不应在整个页面的padding上修改尺寸,这个尺寸应该是一开始原型图就规定好的全局样式)。

4、使用GsonFormat插件生成实体类时,整个实体类应放在bean文件夹下。

5、使用Butterknife注解布局时,可以使用Android Butterknife Zelezny插件自动生成注解。

6、需要提交多个模块代码时,按模块多次提交(也方便填写提交信息)。

7、空页面应该有空页面图片提示。

8、支付宝沙箱环境测试,需要在页面启动前添加这么一句代码EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);

9、

    //将字符串转换成Bitmap类型

 public static Bitmap stringtoBitmap(String string){ Bitmap bitmap=null; try { byte[]bitmapArray; bitmapArray= Base64.decode(string, Base64.DEFAULT); bitmap= BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length); } catch (Exception e) { e.printStackTrace(); } return bitmap; }

 

10、在完成一个版本上线后,应至少分成两个分支,一个日常修复bug以及紧急上线,另一个用于正常功能开发。

11、如果一个接口不需要传参,应设计为传一个空参(例如new Object()),而不是不传参数,这样方便以后拓展接口。

12、adapter中所有的变化的view或值,都应该在viewholder中定义,并在onBinderView中赋值。

13、预览时选择Project Themes,同时gradle中应使用compile而不是implementation。

android项目中常见问题_第1张图片

14、沉浸式状态栏需要设置主题为

 

    
    


另v19设置主题为

 

 

    
    


如果还需要使状态栏中的电量等都隐藏,需要在使用的activity代码中设置

 

 

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

15、进行了某个操作想退出应用,可以使用这样的技巧

 

 

            //回到桌面
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_HOME);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);

16、textview设置滚动,第一步现在XML中设置scrollbars属性,第二步在代码中设置textView.setMovementMethod(ScrollingMovementMethod.getInstance());

17、setOffscreenPageLimit(0)没有效果,最小是1,也就是最小左右各一预加载。

18、调用webview的页面应及时销毁,防止内存泄漏(具体如下):

 

    @Override
    protected void onDestroy() {
        try {
            if( webView!=null) {
                ViewParent parent = webView.getParent();
                if (parent != null) {
                    ((ViewGroup) parent).removeView(webView);
                }
                webView.stopLoading();
                // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
                webView.getSettings().setJavaScriptEnabled(false);
                webView.clearHistory();
                webView.clearView();
                webView.removeAllViews();
                webView.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }

19、WebView的一些相关设置

 

        WebSettings webSettings = webView.getSettings();

        //支持获取手势焦点,输入用户名、密码或其他
        webView.requestFocusFromTouch();

        webSettings.setJavaScriptEnabled(true);  //支持js
        //webSettings.setPluginsEnabled(true);  //支持插件

        //设置自适应屏幕,两者合用
        webSettings.setUseWideViewPort(true);  //将图片调整到适合webview的大小
        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小


        webSettings.setSupportZoom(true);  //支持缩放,默认为true。是下面那个的前提。
        webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。
        //若上面是false,则该WebView不可缩放,这个不管设置什么都不能缩放。

        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件

        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局
        webSettings.supportMultipleWindows();  //多窗口
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);  //关闭webview中缓存
        webSettings.setAllowFileAccess(true);  //设置可以访问文件
        webSettings.setNeedInitialFocus(true); //当webview调用requestFocus时为webview设置节点
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true);  //支持自动加载图片
        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
        //允许自动播放多媒体
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            webSettings.setMediaPlaybackRequiresUserGesture(false);
        }

        //从Android5.0开始,WebView默认不支持同时加载Https和Http混合模式
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }

20、scrollView设置android:fillViewport="true",使scrollview的子控件能够充满屏幕。

21、gradle编译报错

 

Error:Failed to open zip file.
Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
Re-download dependencies and sync project (requires network)
Re-download dependencies and sync project (requires network)

Windows下需要打开AndroidStudio的Files——>Settings——>Build...——>Gradle,手动设置gradle位置。
 

22、将弹出的软键盘的回车键改为搜索键

 

            

其中android:imeOptions需要配合android:inputType属性(或者singleLine属性,PS:单独设置maxLines并不能解决问题)才能使回车键变为需要的图标。

        etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    initData();
                    return true;
                }
                return false;
            }

        });

23、TableLayout中的tableRow中的子控件width设置match_parent(如果子控件的内容小于宽度则不会撑满整个宽度,大于宽度则会显示在屏幕外),解决方法一:在tableLayout中设置android:stretchColumns="对应列",解决方法二:部分情况下可以把该子控件的width设置为wrap_content。

24、注意dialog和popupWindow可能引起的windowleak。

25、android项目中常见问题_第2张图片
这个内存泄漏的问题找了好久,结果发现是因为开启了Android Profiler的原因

android项目中常见问题_第3张图片

就是勾选了这个的原因。

26、在android5.1(API22)及以下的手机上出现了GridLayout不显示的问题,使用V7包下的没问题,可以使用支持包时应优先使用支持包?

27、OKHttp优点:a可以使用GZIP压缩减少传输的数据量;b可以缓存响应避免重复的网络请求;c可以使用拦截器预处理请求与响应;d可以尝试服务器的多个IP地址。

28、retrofit优点:可以使用注解的方式提供功能:请求方法注解、标记类注解、参数类注解。

29、RxJava优点:a、在与okhttp配合时异步写法更简便(不需要使用call.enqueue(callback)的形式);b、在与okhttp配合时请求
和响应可以放在合适的线程中处理(请求在Schedulers.io()这个无限线程池中处理,响应发送到AndroidSchedulers.mainThread()

主线程中处理);c、RxBus事件总线框架(面向事件过程编程,更好的解耦模块)。

30、a三方能使用的资源有应用程序资源、系统资源和厂商资源(是通过Native方法addAssetPathNative()方法添加的);b查找资
源ID对应的不是文件会返回对应的资源字符串;c查找资源ID对应不是文件分为三步:1查找资源文件、2构建XmlResourceParser对

象、3解析文件内容创建view(如果为merge就会减少一层UI嵌套)。

31、android同一个activity下不同fragment的xml布局的id应不同,在组件化的项目中更要注意id的命名(有时候真的项目太紧,才导

致的这些错误)。

32、项目一应该有三种分支,稳定或已发布版本分支,用于修复bug以及随时紧急上线分支,功能开发分支。

33、EasyPermission使用时被@AfterPermissionGranted注解的方法不能含有参数,参考https://stackoverflow.com/question

s/36885517/failure-delivering-result-cannot-execute-non-void-method-opencamera

34、组件化中功能模块的drawable文件以具体功能命名,基础库中以形状颜色命名。

35、recyclerview和listview有时会调用多次onCreateViewHolder或者getView,可能是把高度设置为wrap-content的原因,参

考https://blog.csdn.net/self_study/article/details/42297573和https://blog.csdn.net/self_study/article/details/45694263。

36、对于可能会改变本地数据库和后台数据库记录的操作,都应该防止重复操作。

37、手机开发者选项中不保留活动开关。

38、读Android 动态代理以及利用动态代理实现 ServiceHook有感,实现思路如下:a、通过反射移除系统的某项服务,再添加进我们自定义的服务,这样系统想调用这项服务时会改为调用我们自定义的服务;b、让我们自定义的服务动态代理原来的系统服务,这样最后实际上系统还是会调用系统服务,只不过我们调用前后有了处理的手段。c、使用动态代理的原因就是系统服务

方法众多且未知,使用静态代理不切实际。

39、Can not perform this action after onSaveInstanceState异常,重写activity的onSaveInstanceState()方法去掉默认的super。

40、部分手机两个调用系统相机APP的应用不能同时调用,需要自定义相机。

41、因为activity回调方法有PersistableBundle参数导致otto反射的NoClassDefFoundError: android/os/PersistableBundle异常。

42、dialog主题默认设置为Theme.AppCompat.Light.Dialog,同时自定义dialog需要适配5.0以下版本必须指定style,即

自定义dialog构造方法中:
super(context,R.style.DialogStyle);
styles.xml文件中
    

43、constraintlayout中的app:layout_constraintLeft_toLeftOf="parent"和app:layout_constraintEnd_toEndOf="parent"配合起来用
会使布局靠近parent的右侧。
 


        android:layout_width="wrap_content"                    
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
等价于
        android:layout_width="0dp"//或者wrap_content
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintEnd_toEndOf="parent"


44、viewpager中的fragment的一些设置技巧:

public class FragmentInViewPager extends Fragment {
    private Object netData;
    private boolean isCreated = false;

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        if (!isVisibleToUser && isCreated) {
            //从该界面中离开的一些操作
        }
        super.setUserVisibleHint(isVisibleToUser);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (netData == null) {
            //请求网络获取数据
        } else {
            setData(netData);
        }
        isCreated = true;
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    private void setData(Object netData) {
        //UI数据设置
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        isCreated = false;
    }
}

45、popupwindow.setFocusable(false)设置在showAsDropDown前面才有效。

                mPopup.setFocusable(false);
                mPopup.showAsDropDown(etText, 0, 0);

46、scrollview包含recycleview的一些常见问题解决方案(注意使用NestedScrollView代替scrollview):

recyclerView.setLayoutManager(new LinearLayoutManager(this){
      @Override
      public boolean canScrollVertically() {
         //解决ScrollView里存在多个RecyclerView时滑动卡顿的问题
         //如果你的RecyclerView是水平滑动的话可以重写canScrollHorizontally方法
         return false;
      }
});
//解决数据加载不完的问题
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(true);
//解决数据加载完成后, 没有停留在顶部的问题
recyclerView.setFocusable(false);

47、SP小心不要使用重复key值

48、FragmentPagerAdapter刷新不了,debug可以发现刷新后不会重走getItem方法,这时候可以使用FragmentStatePagerAdapter来替换FragmentPagerAdapter。
49、design拓展包下的TabLayout原来的mTabStrip字段,在28版本改为slidingTabIndicator字段,如果是通过反射获取该字段的,需要根据版本选择不同的字段,例如:


    /**
     * 通过反射设置tablayout左右间距
     * @param tabs tablayout
     * @param leftDip 左间距
     * @param rightDip 右间距
     */
    public static void setIndicator(TabLayout tabs, int leftDip, int rightDip) {
        Class tabLayout = tabs.getClass();
        Field tabStrip = null;
        try {
            tabStrip = tabLayout.getDeclaredField("slidingTabIndicator");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        tabStrip.setAccessible(true);
        LinearLayout llTab = null;
        try {
            llTab = (LinearLayout) tabStrip.get(tabs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics());
        int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics());

        for (int i = 0; i < llTab.getChildCount(); i++) {
            View child = llTab.getChildAt(i);
            child.setPadding(0, 0, 0, 0);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
            params.leftMargin = left;
            params.rightMargin = right;
            child.setLayoutParams(params);
            child.invalidate();
        }
    }

50、recyclerview的上拉加载更多推荐使用以下方式

recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE
                        && !recyclerView.canScrollVertically(1)
                        && !isLoading) {
                    isLoading = true;
                    //上拉加载更多操作
                    //...
                } else if (newState == RecyclerView.SCROLL_STATE_IDLE
                        && !recyclerView.canScrollVertically(-1)
                        && !isLoading) {
                    isLoading = true;
                    //下拉刷新操作,也可以使用SwipeRefreshLayout提供的下拉刷新
                    //...
                }
            }
        });

如果不满一屏无需上拉加载更多推荐使用以下方式

recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE
                        && recyclerView.canScrollVertically(-1) 
                        && !recyclerView.canScrollVertically(1)
                        && !isLoading) {
                    isLoading = true;
                    //上拉加载更多操作
                    //...
                }
            }
        });

51、fragment重叠的问题

    private static final String UNIT_DYNAMIC_MEMBER_KEY = "unitDynamicMemberFragment";
    private static final String UNIT_DYNAMIC_UNIT_KEY = "unitDynamicUnitFragment";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            /*获取保存的fragment  没有的话返回null*/
            unitDynamicMemberFragment = (UnitDynamicMemberFragment) getSupportFragmentManager().getFragment(savedInstanceState, UNIT_DYNAMIC_MEMBER_KEY);
            unitDynamicUnitFragment = (UnitDynamicUnitFragment) getSupportFragmentManager().getFragment(savedInstanceState, UNIT_DYNAMIC_UNIT_KEY);
        }
        ...
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (unitDynamicMemberFragment != null) {
            getSupportFragmentManager().putFragment(outState, UNIT_DYNAMIC_MEMBER_KEY, unitDynamicMemberFragment);
        }
        if (unitDynamicUnitFragment != null) {
            getSupportFragmentManager().putFragment(outState, UNIT_DYNAMIC_UNIT_KEY, unitDynamicUnitFragment);
        }
        super.onSaveInstanceState(outState);
    }

52、Butterknife原理,先利用编译时注解AbstractProcessor(同时需要在build.gradle中设置编译时运行的代码)生成类;然后再利用bind()方法调用生成的类文件。
53、为节省流量进行的图片处理:
        上传时进行尺寸压缩,质量压缩;
        加载时通过?x-oss-process=image/resize,w_1440进行阿里云OSS图片处理,具体尺寸根据图片实际在屏幕中的像素来决定(例如宽占屏幕三分之一,就传?x-oss-process=image/resize,w_480),横屏时通常传h_1440;
        另外使用Picasso或者Glide的图片加载框架进行了多级缓存,其中通过请求头设置进行了网络缓存:
        第一次请求时没设置if-none-match:
android项目中常见问题_第4张图片

         再次请求时请求头添加了 if-none_match,比较服务器上是否有对应ETag的文件,如果有则直接从缓存中获取图片无需网络返回:
android项目中常见问题_第5张图片

54、从 Activity A跳到B,再从B跳到C,并且希望 A中可以直接拿到C返回的结果,而B只是起到一个过渡的作用。在B跳C的intent中使用FLAG_ACTIVITY_FORWARD_RESULT标志。

你可能感兴趣的:(Android经验之谈)