Android内存优化建议

谈到性能问题,你可能首先就想到的是麻烦或者头大,其实我有时候也非常头疼,因为性能优化涉及的太广,因此一时间无从下手,就像我一样特别是项目里涉及到底层代码时,就更加不知所措,那么我们如何来进行优化?其实引用Android官网的建议,无非就是以下,说的好轻松,可是实际操作还是有很多学问,因为时间原因,我总结了一点自己在项目里的优化建议。
  • 不要做冗余的工作
  • 尽量避免次数过多的内存分配操作
  • 深入理解所有语言特性和系统平台的API,具体到Android开发,熟练掌握java语言,并对SDK的API熟悉,了如指掌

一、谨慎使用Service

项目用到Service执行不是长期任务时,任务结束后一定要停止该Service,否则会一直占用内存,也可以使用IntentService來代替,因为它执行完成会自动停止。


二、Bitmap对象和ImageView

1:Bitmap的优化无非就是进行压缩,比如页面仅仅是一个很小的ImageView,此时展示高清图片没有任何意义,而且占用内存过大,需要根据项目要求对图片进行合理的压缩,具体如何压缩网上一大堆资料,如果涉及到创建Bitmap对象时,使用完手动recycle()清除资源

2:开发中经常在xml里或者代码指定本地图片/网络图片,当不使用时,可以主动调用以下代码进行释放资源

BitmapDrawable db = (BitmapDrawable)ImageView.getDrawable()
//BitmapDrawable db =(BitmapDrawable)ImageView.getBackground()
Bitmap bp = db.getBitmap();
bp.recycle();
bp = null;

三、布局优化

1:减少嵌套,优先使用LinearLayout实现,当LinearLayout需要多层嵌套才能完成时,建议使用RelativeLayout实现。

2:使用include标签实现布局复用减少嵌套,比如显示断网的布局,几乎每个页面都使用到,可以将该布局单独定义出来,使用者使用include来引入。

好处1->是避免重复代码提高复用性。

好处2->维护方便,哪天领导说换个图片,只需要修改一个地方即可。

3:使用merge标签包裹(当View的父View是LinearLayout布局,而父View的父View也是LinearLayout,方向一致时),这样可以去除多余相同的

比如定义merage_demo_item文件是很多地方可以共用的,布局排版是线性的垂直方向,里面两个按钮

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="被引入的View1"
            android:textColor="@android:color/white"
            android:background="#666060"
            android:textSize="20sp"
            android:gravity="center"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="被引入的View2"
        android:textColor="@android:color/white"
        android:background="#666060"
        android:textSize="20sp"
        android:gravity="center"/>
</merge>
刚好引入的地方它的父容器也是线性垂直方法
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    
    <include
        layout="@layout/merage_demo_item"/>
</LinearLayout>
这样也能实现效果
如果我们不用merage标签,而是在 merage_demo_item文件里顶层使用LinearLayout虽然也是可以实现同样效果,但是会多嵌套一层,性能有所下降。

3:使用ViewStub标签进行懒加载,虽然定义在xml里,但是系统不会加载到内存里,当我们使用的时候才进行加载。

<ViewStub
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/viewstub_state"/>


四、其它优化

1、对字符串的+拼接会导致创建很多临时对象非常影响性能,强烈建议使用StringBuilder或者StringBuffer完成。

2、for循环的使用适当结束循环

for( int i=0; i<arr.size(); i++ ){
     if("".queals(arr.get(i)))
             break;	//条件满足时,一定要结束循环
}

3、SQLite数据库优化 

当我们给数据库插入数据时,每插入一次数据,就会操作一次数据库,插入多少数据就直接对磁盘操作多少次,大大降低了效率。

如何进行优化?

1、1:事务优化

SqliteDataBase.beginTransaction()开启事务

SqliteDataBase.setTransactionSuccessful()设置事务成功

SqliteDataBase.endTransaction()结束事务

操作数据库之前开启事务,操作完成设置事务成功,最后在结束事物,结束事务的时候会判断事务有没有成功,如果成功则一次批量执行,否则回滚到之前。

正常操作代码

long startTime = System.currentTimeMillis();
        String table = "user";
        for (int i = 0; i < 100; i++) {
            ContentValues contentValues = new ContentValues();
            contentValues.put("name", "张三" + i);
            contentValues.put("age", i);
            sqLiteDatabase.insert(table, null, contentValues);
        }
        long endTime = System.currentTimeMillis();
        String message = "总共耗时" + (endTime - startTime);
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        Log.v(Contacts.TAG, message);	//1457ms
优化后代码

long startTime = System.currentTimeMillis();
        String table = "user";
        /**开启一个事务**/
        sqLiteDatabase.beginTransaction();
        try {
            for (int i = 0; i < 100; i++) {
                ContentValues contentValues = new ContentValues();
                contentValues.put("name", "张三" + i);
                contentValues.put("age", i);
                sqLiteDatabase.insertOrThrow(table, null, contentValues);
            }
            /**将数据库事务设置为成功**/
            sqLiteDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            /**结束数据库事务**/
            sqLiteDatabase.endTransaction();
        }
        long endTime = System.currentTimeMillis();
        String message = "总共耗时" + (endTime - startTime);
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        Log.d(Contacts.TAG, message);	//68ms
正常执行需要1457ms,优化之后只需要68ms

  1、2使用原生SQL语句

系统提供了一些封装方法比如insert()、upDate()、delete()等,查询源码发现这些,里面一大堆判断拼接SQL语句,相比我们直接使用原始SQL语句效率越低。

1、3使用SQLiteStatementt预编译对象

通过查看源码,无论我们怎么操作数据库,最终都是创建了SQLiteStatement对象来操作对应的方法,结合事务可以使效率更快,两个方法各运行了10次得到的总耗时对比,代码如下

public void saveUser3() {
		long start = SystemClock.currentThreadTimeMillis();
		SQLiteDatabase db = mHelper.getWritableDatabase();
		db.beginTransaction();
        for (int i=0;i<300;i++){
            ContentValues values = new ContentValues();
            values.put("name", "事务"+i);
            values.put("pwd", "");
            db.insert("mAccount", null, values);
		}
		db.setTransactionSuccessful();
		db.endTransaction();
		db.close();	//47 34 44  44 45 46 49 48 48 48  = 453
		Log.v("wjw", "事务优化===" + (SystemClock.currentThreadTimeMillis() - start));
	}

    public void insert4() {
        long start = SystemClock.currentThreadTimeMillis();
        SQLiteDatabase db = mHelper.getWritableDatabase();
        db.beginTransaction();
        String sql = "INSERT INTO mAccount(name,pwd) values(?,?)";
        for (int i=0;i<300;i++){
            SQLiteStatement statement = db.compileStatement(sql);
            statement.bindString(1,"Statement"+i);
            statement.bindString(2,"");
            statement.executeInsert();
        }
        db.setTransactionSuccessful();
        db.endTransaction();
        db.close(); //46 23 29 45 38 35 31 33 38 34 = 352
        Log.v("wjw", "事件+SQLiteStatement优化===" + (SystemClock.currentThreadTimeMillis() - start));
    }

1、3使用索引

比如表名table,里面有name字段,当你查询name=张三时,会全局遍历直到添加满足,假设给name添加索引,内部会根据索引进行获取,详细使用情况可以网上查看。


五、过渡绘制

1、View重叠,比如背景是白色,上面叠一个View,View又设置一个背景,同一个地方绘制了两次。

2、消除默认背景,在Activity的onCreate中getWindow().setBackroundDrable(null);

3、ClipRect使用


六、线程池

为什么使用线程池?

在Java里,创建和销毁线程都是很大的开销,可能消耗的资源比我们执行任务用的更多,这个时候就要考虑线程的复用,线程池里的线程执行完任务会处于等待状态,达到线程复用。

线程池结构:

线程池管理:管理创建,销毁,添加任务等。

线程:线程池中的线程,如果是没有任务的时候,会一直处于等待。

任务接口:每个任务实现的接口,方便任务的执行。

任务队列:缓存机制,存放一些没有处理的任务。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值