记一场 Android 技术答疑
Trinidad36M
8年前
<p>之前在Stuq的Android课程中有幸分享了一些关于优化的问题,后期又处理了一些来自网友的问题,这里简单以文字形式做个整理.</p> <h2><strong>网络IO应该在哪种形式的线程中执行</strong></h2> <ul> <li> <p>首先网络IO一般耗时比较长,有的可能到几十毫秒</p> </li> <li> <p>由于耗时较长,如果采用单一线程处理,势必导致后续的请求无法快速执行</p> </li> <li> <p>建议使用线程池来处理达到快速响应和线程的复用。</p> </li> </ul> <p>简单示例:</p> <pre> <code class="language-java">private void testDoNetworkRequest() { int corePoolSize = 5; int maxPoolSize = 10; //线程数量超过核心线程数之后的超时时间,即超过这个时间还没有新的task,多余的线程则销毁掉。 long keepAliveTime = 10; ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()); executor.execute(new Runnable() { @Override public void run() { //Do network IO here } }); }</code></pre> <h2><strong>如何优化字符串拼接</strong></h2> <ul> <li> <p>字符串拼接无法避免的创建StringBuilder对象</p> </li> <li> <p>如果是循环情况下拼接,需要显式在循环外声明一个StringBuilder对象</p> </li> </ul> <h3><strong>不好的代码</strong></h3> <pre> <code class="language-java">public void implicitUseStringBuilder(String[] values) { String result = ""; for (int i = 0 ; i < values.length; i ++) { result += values[i];//create new StringBuilder object every time } System.out.println(result); }</code></pre> <h3><strong>改进后的代码</strong></h3> <pre> <code class="language-java">public void explicitUseStringBuider(String[] values) { StringBuilder result = new StringBuilder(); for (int i = 0; i < values.length; i ++) { result.append(values[i]); } }</code></pre> <h2><strong>使用Handler到底需不需要使用弱引用,什么时候情况下用</strong></h2> <ul> <li> <p>正常境况下的引用都为强引用,其特点是及时内存溢出也不可以被回收</p> </li> </ul> <pre> <code class="language-java">ArrayList list = new ArrayList();</code></pre> <ul> <li> <p>弱引用则会在垃圾回收时被回收掉,因而弱引用解决内存泄露的一种方法。</p> </li> </ul> <pre> <code class="language-java">ArrayList list = new ArrayList(); WeakReference<ArrayList> listWeakRef = new WeakReference<ArrayList>(list); ArrayList myList = listWeakRef.get();</code></pre> <ul> <li> <p>Handler是否需要设置弱引用,取决于它是否可能发生内存泄露</p> </li> </ul> <h3><strong>Handler内存泄露的场景</strong></h3> <ul> <li>Message的target变量实际是Handler对象</li> <li>Message存放在MessageQueue中</li> <li>MessageQueue通常为Looper持有</li> <li>Looper和可以认为和线程生命周期相同</li> <li>通常情况下,我们使用匿名内部类的形式创建Handler,而匿名内部类(非静态内部类)会隐式持有外部类的引用。即如下的mHandler会隐式持有Activity的实例引用。</li> </ul> <pre> <code class="language-java">private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };</code></pre> <ul> <li> <p>如果有一个延迟很久的消息,可能会导致Activity内存泄露</p> </li> <li> <p>可以使用弱引用解决内存泄露问题</p> </li> <li> <p>也可以在Activity onDestory方法中调用handler.removeCallbacksAndMessages(null);</p> </li> </ul> <h2><strong>网络数据返回先通知界面还是先更新数据库</strong></h2> <ul> <li>通常境况下,可以选择先更新界面再更新数据库</li> <li>如果数据很重要,建议先更新数据库在通知界面更新</li> </ul> <h2><strong>业务场景:需要定时后台扫描数据库,上传本地照片至云端,定时任务采用何种模式</strong></h2> <ul> <li> <p>Handler或者Timer定时一般为秒级别的任务,Timer会启动额外线程,而Handler可以不用。</p> </li> <li> <p>无论是Handler还是Timer都需要依赖于进程存活</p> </li> <li> <p>利用Handler实现定时任务的类: HandlerTimer</p> </li> <li> <p>如果时间较长,则需要使用AlarmManager</p> </li> <li> <p>另外,我们对于这种业务应该优先考虑是否可以基于事件通知。</p> </li> <li> <p>如果是加入媒体库的文件,我们可以使用registerContentObserver监听媒体库文件变化。</p> </li> </ul> <h2><strong>static 单例是怎么保证单例的?没太看明白</strong></h2> <ul> <li> <p>static变量为类所有</p> </li> <li> <p>staitc只初始化一次,即在调用的时候。</p> </li> <li> <p>如下代码,STATIC_OBJECT只在第一次调用时初始化,后续调用则不会再执行初始化</p> </li> </ul> <pre> <code class="language-java">public class Example { public static Object STATIC_OBJECT = new Object(); }</code></pre> <ul> <li>使用static机制创建单例</li> </ul> <pre> <code class="language-java">public class SingleInstance { private SingleInstance() { } public static SingleInstance getInstance() { return SingleInstanceHolder.sInstance; } private static class SingleInstanceHolder { private static SingleInstance sInstance = new SingleInstance(); } }</code></pre> <h2><strong>把Activity作为参数传给一个静态方法,会影响这个Activity的正常销毁吗</strong></h2> <ul> <li> <p>内存泄露与方法是否是静态与否无关,与内部的方法体实现有关系。</p> </li> <li> <p>内存泄露可以简单理解成:生命周期长的对象不正确持有了持有了生命周期短的对象,导致生命周期短的对象无法回收。</p> </li> <li> <p>比如Activity实例被Application对象持有,Activity实例被静态变量持有。</p> </li> </ul> <h2><strong>Bitmap优化</strong></h2> <ul> <li> <p>options.inJustDecodeBounds = true;可以获取width,height和mimetype等信息,但不会申请内存占用</p> </li> <li> <p>合理进行缩放,一个高分辨率的图片不仅展示在一个小的imageView中,不仅不会有任何视觉优势,反而还占用了很大的内存</p> </li> <li> <p>将Bitmap处理移除主线程</p> </li> <li> <p>使用LruCache或者DiskLruCache缓存Bitmap</p> </li> <li> <p>before 2.3 手动调用recycle()方法</p> </li> </ul> <p>多次在生产签名打包后的apk,出现功能不可用的情况,比方说有个社会化分享功能,写代码时都可以正常实现,但签名生成apk后该功能无法再使用了,点击分享面板的平台,没有任何响应。请问是怎么回事,这种问题解决应该从哪几个方面入手,希望有一些思路可供参考</p> <ul> <li> <p>应该是混淆引起的</p> </li> <li> <p>混淆是将易读性较好的变量,方法和类名替换成可读性较差的名称</p> </li> <li> <p>混淆的目的是为了加大逆向的成本,但不能避免</p> </li> <li> <p>通常混淆的处理是将某些库不加入混淆</p> </li> <li> <p>第三方的库不建议混淆</p> </li> </ul> <h3><strong>一些需要排除混淆的</strong></h3> <ul> <li> <p>被native方法调用的java方法</p> </li> <li> <p>供javascript调用的java方法</p> </li> <li> <p>反射调用的方法</p> </li> <li> <p>AndroidManifest中声明的组件</p> </li> <li> <p>总结:即所有硬编码的元素(变量,方法,类)</p> </li> </ul> <p> </p> <p> </p> <p>来自:http://www.udpwork.com/item/15888.html</p> <p> </p>