Android 部分
内存优化、OOM 异常的检测以及处理
图片的处理
- 软引用处理图片
简单自己用WeakReference做一个bitmap缓存池,也可以用类似图片加载库写一个通用的bitmap缓存池
- 图片压缩
BitmapFactory 在解码图片时,可以带一个Options,有一些比较有用的功能,比如:
inSampleSize:压缩图片
inJustDecodeBounds : 获得图片大小
inPreferredConfig : 图片模式,默认会使用ARGB_8888
数据结构优化
- ArrayMap、SparseArray 替换HashMap
ArrayMap及SparseArray是android的系统API,对于key为int的HashMap尽量使用SparceArray替代,大概可以省30%的内存;ArrayMap对内存的节省实际并不明显,10%左右,但是数据量在1000以上时,查找速度可能会变慢。
- 内存抖动:
主要原因还是有因为大量小的对象频繁创建,导致内存碎片,从而当需要分配内存时,虽然总体上还是有剩余内存可分配,而由于这些内存不连续,导致无法分配,系统直接就返回OOM了。
例如 :String频繁拼接
改进方法:StringBuilder拼接字符串
对象泄漏和对象复用
单例(主要原因还是因为一般情况下单例都是全局的,有时候会引用一些实际生命周期比较短的变量例如 Activity的 Context,导致 Activity 虽然 finish 了却无法释放其占用的内存单例内存泄漏)
静态变量(同样也是因为生命周期比较长,GC 无法回收)
Handler内存泄露
- 在 Java 中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象
- 方式:1) 静态内部类 2) 软引用)
匿名内部类(匿名内部类会引用外部类,导致无法释放,比如各种回调)
资源使用完未关闭(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)
ListView convertView复用
onDraw()避免执行对象的创建
内存检测办法
利用 LeakCanary 来检查 Android 内存泄漏内存泄漏
在Application中配置,可以检测 Activity 和 Fragment 等的内存泄漏,在 Logcat 中打印出来。
布局优化
调优工具:hierarchy viewer、
viewstub:默认不会加载,手动 inflate 才会加载出来。viewstub
使用RelativeLayout代替LinearLayout,减少层级.
在不影响层级深度的情况下,使用LinearLayout而不是RelativeLayout。因为RelativeLayout会让子View调用2次。
使用TextView的行间距:lineSpacingExtra. 可以同时设置多行文字,中间用/n 分开
用TextView同时显示图片和文字:drawableleft…
使用Spannable或Html.fromHtml:设置一行文字不同颜色,不同大小
用LinearLayout自带的分割线 :xml 中设置 divider 属性
Space控件:设置条目间距。使用过多的margin其实会影响代码的可读性。
merge标签
a. 布局顶结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替。
b. 某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。
自定义控件
步骤:
构造函数中:1. 获得控件属性(arr.xml)2. 初始化画笔
重写onMeasure()设置控件大小 : 不设置的话,wrap_content和 match_parent 都是占满屏幕
其中有三种模式:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
onLayout()
- ViewGroup才有这个方法
- 作用:摆放子控件位置
ondraw():画界面
界面刷新的几个方法
requestLayout:View重新调用一次layout过程。
invalidate:View重新调用一次draw过程,不能直接在线程中调用
postInvalidate():在主线程可以调用,内部实现了 handler
事件分发传递机制
ViewPager + Fragment 的使用,及懒加载的实现
Handler 异步消息机制以及原理
异步消息处理机制主要由Message,Handler,MessageQueue和Looper这四部分组成
- Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据
- Handler是处理者的意思,它主要用于发送和处理消息,发送消息通过Handler的sendMessage()方法,发出的消息最终会被传递到Handler的handleMessage()方法中
- MessageQueue是消息队列,用来存放Handler发送的消息,这些消息一直存放在消息队列中等待被处理,每个线程只有一个MessageQueue对象
- Looper是专门来从MessageQueue中取出消息的,调用Looper的loop()方法后,就会不断的从消息队列中取出消息传递到Handler的handleMessage()方法中,而每个线程中也只有一个Looper对象
屏幕适配方案
原因:碎片化严重。
一些基本单位
dpi:dot per inch(每英寸像素点数)
计算方法:
nexus 5
1920 * 1080
4.95inch
dpi = (1920^2 + 1080^2)开根号/4.95 = 445
px :像素
dp、dip,在160dp 下,1dp=1px
sp 可以根据文字大小首选项进行缩放。(不要奇数,不要小数)
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi
解决方案1:支持屏幕尺寸:
使用 wrap_content、match_parent、weight(weight 的计算公式)
使用相对布局
尺寸限定符 :在 layout和 layout-sw600dp(最小宽度限定符) 下分别设置两个名字相同的布局 缺点:维护两套布局
布局别名:先了解。
屏幕方向限定符:
res/values-sw600dp-land/layouts.xml:
res/values-sw600dp-port/layouts.xml:
- .9图
解决方案2:使用支持各种屏幕密度
- 解决屏幕宽度不一致的问题:新建多个 values-480*320(各种分辨率)文件,里面对应 单位对应的 px 值;然后设置默认 values 值,设置对应单位对应 dp 的值,防止个别设备对应不上的现象。
- 提供备用位图:图片放到不同 drawable文件夹,内存占用不同。
解决方案3:实施自适应用户界面流程
图片加载的优化
数据结构相关知识
ViewPager 无限轮播的实现
思路:
- 在第一张图片前和最后一张图片后分别添加一个ImageView
- 最前边的ImageView背景设置为最后一张图片,最后一个ImageView背景设置第一张图片。
- 当我们判断滑动到最后一个ImageView时则设置ViewPager.setCurrentItem(1),让其自动切换到第一张图片,这样在从最后一页切换到第一页时由于图片是用的同一张图片,所以就会使切换效果显得很流畅自然。
- 同理,当滑动到第0个ImageView时用ViewPager.setCurrentItem(length)自动切换到倒数第二张图片,第0个ImageView和倒数第二个ImageView图片相同,这样就使滑动效果显得很自然。
友盟多渠道打包
- 添加对友盟库的依赖
- 在Manifest.xml中去声明我们的appkey,以及我们的渠道占位符
- 在我们应用的build.gradle中去动态的替换掉我们占位符的value即可
- 控制台./gradlew assemableRelease自动打包
Listener写法
写一个接口OnClickListener,包含需要回调的方法。
例如:onClick(View view , int position)
被监听的类 B
- 提供外部方法 setOnClickListener(OnClickListener listener)
OnClickListener是接口,负责回调数据到主动监听的 Class
- 新增本地变量mListener,接受1中传递过来的 listener
mListener = listener
- 逻辑处理完直接调用接口中的方法,传入参数执行回调。
例如:mListener.onClick(view , position)
监听类 A
new B().setOnClickListener( -v { @Override public void onClick(View view , int position){ 这样就拿到了回调的数据 } });
Layout_weight计算
计算方法:计算出的宽度= 原来宽度+剩余空间所占百分比的宽度
现在有一个屏幕宽度是480dp,三个 TextView 分别如下。
textview1:
width = match_parent height = 48d weight = 1
textview2:
width = match_parent height = 48dp weight = 2
textview3:
width = match_parent height = 48dp weight = 2
首先计算剩余空间是:rest = 480 - 480 * 3 = -480 * 2
最后计算的比例如下所示。
- textview1:480 + (-480 * 2)(1/5) = 480 3/5
- textview2:480 + (-480 * 2)(2/5) = 480 1/5
- textview2:480 + (-480 * 2)(2/5) = 480 1/5
支付宝
- 申请账户,返回四个值。分别是:
- 私钥(私钥不要放到代码中)
- 公钥
- AppID
- 商户支付宝账号
- 发待支付的订单信息给服务器,让服务器签名(服务器拿着私钥)
- 服务端返回签名信息
- 开启子线程
Runnable payRunnable = new Runnable() { @Override public void run() { // 构造PayTask 对象 PayTask alipay = new PayTask(MainActivity.this); // 调用支付接口,获取支付结果 String result = alipay.pay(payInfo, true); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } };
- 在handler中接受支付的回掉信息
private Handler mHandler = new Handler() { @SuppressWarnings("unused") public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { PayResult payResult = new PayResult((String) msg.obj); /** * 同步返回的结果必须放置到服务端进行验证(验证的规则请看https://doc.open.alipay.com/doc2/ * detail.htm?spm=0.0.0.0.xdvAU6&treeId=59&articleId=103665& * docType=1) 建议商户依赖异步通知 */ String resultInfo = payResult.getResult();// 同步返回需要验证的信息 String resultStatus = payResult.getResultStatus(); // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档 if (TextUtils.equals(resultStatus, "9000")) { Toast.makeText(MainActivity.this, "支付成功", Toast.LENGTH_SHORT).show(); } else { // 判断resultStatus 为非"9000"则代表可能支付失败 // "8000"代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态) if (TextUtils.equals(resultStatus, "8000")) { Toast.makeText(MainActivity.this, "支付结果确认中", Toast.LENGTH_SHORT).show(); } else { // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误 Toast.makeText(MainActivity.this, "支付失败", Toast.LENGTH_SHORT).show(); } } break; } default: break; } } };
微信支付
[android微信支付详解与坑](http://blog.csdn.net/hello_1s/article/details/52636447)
申请微信支付,得到三个参数
//appid 微信分配的公众账号ID public static final String APP_ID = ""; //商户号 微信分配的公众账号ID public static final String MCH_ID = ""; // API密钥,在商户平台设置 public static final String API_KEY= "";(放在后端)
- 发待支付的订单信息给服务器,让服务器生成预订单
- 服务器再次签名,返回json格式的结果
PayReq payReq = new PayReq(); payReq.appId = weiXinPay.getAppid(); payReq.partnerId = weiXinPay.getPartnerid(); payReq.prepayId = weiXinPay.getPrepayid(); payReq.packageValue = weiXinPay.getPackage_exten(); payReq.nonceStr = weiXinPay.getNoncestr(); payReq.timeStamp = weiXinPay.getTimestamp(); payReq.sign = weiXinPay.getSign();
在Activity中定义一个全局的变量:
private IWXAPI api = api = WXAPIFactory.createWXAPI(this, "你在微信开放平台创建的app的APPID");
调取微信支付
api.sendReq(payReq);
创建一个activity
路径为:com.xxx.xxx.wxapi.WXPayEntryActivity implements IWXAPIEventHandler
回掉支付结果的消息
public void onResp(BaseResp baseResp) { Log.e("---------->","code:"+baseResp.errCode); if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { //这里就是支付完成后需要做的事,如跳到哪个页面啥的 } }
百度地图的LBS搜索
Bugly检测错误
- bugly能够检测的类型:常见的错误,空指针,数组越界(最好别说,太low)OOM(对照着内存泄漏来说),ANR(遇到过由于网络请求的Retrofit创建太多而导致ANR异常,后来将Retrofit改为单例模式即可)
设计模式部分
- 项目中运用到那些模式:有点水平的那种:Retrofit的单例模式
public class AppRetrofit { //valatile关键字Retrofit每次都从内存中取而不是从缓冲中取,保证唯一不变性 private volatile static Retrofit retrofit ; private AppRetrofit(){ } public static <T> T getNewsRetrofit(Class<T> clazz, String baseUrl) { if (TextUtils.isEmpty(baseUrl)) { throw new IllegalArgumentException("BaseUrl cannot be null"); } // /** * 判断是否需要缓存数据,默认为false,可以用单利在每次请求之前设置值 */ if (true){ if (retrofit == null){ synchronized (AppRetrofit.class){ if (retrofit == null) retrofit = new Retrofit.Builder() .baseUrl(baseUrl) //刚刚添加进来的请求头 .client(getCacheOkHttpClient(App.getApplication())) //使用缓存,Interceptor截获每次网络请求用于缓存数据 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //添加Rxjava //添加Gson解析 .addConverterFactory(GsonConverterFactory.create()) .build(); } } }else{ Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(getOKHttpClient()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //添加Gson解析 .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit.create(clazz); } }
- Retrofit的动态代理模式(上面有)
- Rxjava的适配器模式(上面也有)
属性动画
- 概述
Android提供了几种动画类型:View Animation 、Drawable Animation 、Property Animation 。View Animation相当简单,不过只能支持简单的缩放、平移、旋转、透明度基本的动画,且有一定的局限性。比如:你希望View有一个颜色的切换动画;你希望可以使用3D旋转动画;你希望当动画停止时,View的位置就是当前的位置;这些View Animation都无法做到。这就是Property Animation产生的原因。
API 使用介绍
- ObjectAnimator直接设置 –效率低
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mImageView, "translationX", 0f,100f);//参数分别为要改变的对象。要改变的参数(只要改对象有对应的set,就可以填相应的名字),从多少到多少 ObjectAnimator.ofFloat(mImageView, "translationY", 0f,100f)//如果同时有两个动画,则会同时进行。 objectAnimator.start();//开始播放动画
- propertyValuesHolder,效率相比方法一高
PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("translationX", 0f,100f);// PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("translationY", 0f,100f); ObjectAnimator.ofPropertyValuesHolder(mImageView, p1,p2).start();//将设置好的参数,赋给ObjectAnimator
- AnimatorSet
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mImageView, "translationX", 0f,100f); ObjectAnimator objectAnimator2=ObjectAnimator.ofFloat(mImageView, "translationY", 0f,100f); ObjectAnimator objectAnimator3=ObjectAnimator.ofFloat(mImageView, "rotation", 0f,360f); AnimatorSet animatorSet=new AnimatorSet(); //animatorSet.playTogether(objectAnimator,objectAnimator2); //设置两个动画一起播放 //animatorSet.playSequentially(objectAnimator,objectAnimator2); //设置两个动画连续播放,即先播放第一个然后播放第二个 //上面两者不能同时出现,否则抛出异常。 animatorSet.play(objectAnimator).with(objectAnimator2);//表示这两个动画同时进行。 animatorSet.play(objectAnimator3).after(objectAnimator2);//表示这个动画在哪个动画播放结束后进行。 animatorSet.start(); //动画开始播放Java 部分
Service 详解
Android Service完全解析,关于服务你所需知道的一切
基本用法:
Intent startIntent = new Intent(this, MyService.class); startService(startIntent); //开启服务 stopService(startIntent); //关闭服务
这时候 service 中依次执行:
onCreate()//只在打开服务时走一次这个方法 onStartCommand()
缺点:Activity和Service没什么关联。
Service和Activity通信
Activity 通知 Service 可以用 Binder调取 Service 里面的方法;
Service 可以用发送广播通知 Activity 更新界面(当然这里也可以使用LocalBroadcastManager ?暂时不了解)。
新建一个MyService.class
public class MyService extends Service { public static final String TAG = "MyService"; private MyBinder mBinder = new MyBinder(); //...省略一些方法 @Override public IBinder onBind(Intent intent) { return mBinder; } class MyBinder extends Binder { public void startDownload() { //自定义方法名称 Log.d("TAG", "startDownload() executed"); // 执行具体的下载任务 } } }
Activity 中:
public class MainActivity extends Activity implements OnClickListener { private Button startService; private Button stopService; private Button bindService; private Button unbindService; private MyService.MyBinder myBinder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { myBinder = (MyService.MyBinder) service; myBinder.startDownload(); //可以调用 service 中的方法 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //...省略 findId 和 click } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bind_service: Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); break; case R.id.unbind_service: unbindService(connection); break; default: break; } } }
备注:
service 运行在主线程,不能耗时太长,除非开启子线程。
用startService打开的服务必须用stopService才能停止;
用bindService打开的服务必须用unbindService才能停止;
先用startService后用bindService必须先stopService再unbindService才能停止服务。
AIDL
定义:
AIDL(Android Interface Definition Language)是Android接口定义语言的意思,
它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
使用步骤:
- 新建MyAIDLService.aidl文件,代码如下所示:
package com.example.servicetest; interface MyAIDLService { int plus(int a, int b); String toUpperCase(String str); }
- 新建MyService.class (在该类中把方法实现好)
public class MyService extends Service { /*这里先是对MyAIDLService.Stub进行了实现,重写里了toUpperCase()和plus()这两个方法。这两个方法的作用分别是将一个字符串全部转换成大写格式,以及将两个传入的整数进行相加。然后在onBind()方法中将MyAIDLService.Stub的实现返回。这里为什么可以这样写呢?因为Stub其实就是Binder的子类,所以在onBind()方法中可以直接返回Stub的实现。*/ ...... @Override public IBinder onBind(Intent intent) { return mBinder; } MyAIDLService.Stub mBinder = new Stub() { @Override public String toUpperCase(String str) throws RemoteException { if (str != null) { return str.toUpperCase(); } return null; } @Override public int plus(int a, int b) throws RemoteException { return a + b; } }; }
- 修改AndroidManifest.xml中的代码,给MyService加上一个action
<service android:name="com.example.servicetest.MyService" android:process=":remote" > <intent-filter> <action android:name="com.example.servicetest.MyAIDLService"/> </intent-filter> </service>
拷贝MyAIDLService.aidl文件到Client端
注意:要将原有的包路径一起拷贝过来
打开或新建MainActivity,在其中加入和MyService建立关联的代码
public class MainActivity extends Activity { private MyAIDLService myAIDLService; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { myAIDLService = MyAIDLService.Stub.asInterface(service); try { int result = myAIDLService.plus(50, 50); String upperStr = myAIDLService.toUpperCase("comes from ClientTest"); Log.d("TAG", "result is " + result); Log.d("TAG", "upperStr is " + upperStr); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bindService = (Button) findViewById(R.id.bind_service); bindService.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.servicetest.MyAIDLService"); //隐式调用 bindService(intent, connection, BIND_AUTO_CREATE); } }); } }
IntentService
介绍:
- IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作
- 当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。
- 可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
如何使用
- 新建 BroIntentService.class
public class BroIntentService extends IntentService { @Override protected void onHandleIntent(Intent intent) { //Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务 String action = intent.getExtras().getString("param"); if (action.equals("oper1")) { System.out.println("Operation1"); }else if (action.equals("oper2")) { System.out.println("Operation2"); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }
在 Activity 中开启服务
public class TestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //可以启动多次,每启动一次,就会新建一个work thread,但IntentService的实例始终只有一个 Intent startServiceIntent = new Intent("com.test.intentservice"); Bundle bundle = new Bundle(); bundle.putString("param", "oper1"); startServiceIntent.putExtras(bundle); startService(startServiceIntent); }
清单文件里注册 Service
备注:多次开启服务依次执行的方法为:
onCreate
onStartCommand
onStart
onStartCommand
onStart
…
onDestory (运行完自动结束)
Broadcast
分类
普通广播
优点:普通广播是完全异步的,可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高
缺点:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播
Context.sendBroadcast()
有序广播
有序广播是按照接收者声明的优先级别依次发送。被接收者依次接收广播。
如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。
对于有序广播,前面的接收者可以将处理结果存放进广播Intent,然后传给下一个接收者。
Context.sendOrderedBroadcast()
LocalBroadcastManager(源码简单,待研究)
Android LocalBroadcastManager的使用
简介
不涉及进程间通讯,不用担心普通广播可能产生的一些安全性问题。
一句话总结
LocalBroadcastManager
:披着广播外衣的Handler
使用方法
LocalBroadcastManager
和普通的广播是一模一样,不同的LocalBroadCastManager
的调用方不再是context
,而是LocalBroadCastManager
的实例。public class Test extends Activity { private static final String ACTION = "simple_action"; private static final String DATA = "data"; BroadcastReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 新建一个receiver mReceiver = new MyReceiver(); // 注册receiver LocalBroadcastManager.getInstance(this) .registerReceiver(mReceiver, new IntentFilter(ACTION)); // 发送消息 Intent messageIntent = new Intent(ACTION); messageIntent.putExtra(DATA, "给xxx的一封信"); LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); } @Override protected void onDestroy() { // 取消注册 LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); } class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 处理消息 Log.i("TAG", "收到一封信:" + intent.getStringExtra(DATA)); } } }
SQLite
介绍两个类
SQLiteOpenHelper 类
DBHelper继承了SQLiteOpenHelper,作为维护和管理数据库的基类
DBManager 类 (Dao)
DBManager是建立在DBHelper之上,封装了常用的增删改查业务方法
具体介绍这两个类
SQLiteOpenHelper 类
- 用
SQLiteOpenHelper
类中的getWritableDatabase()
和getReadableDatabase()
方法可以获得数据库的引用。 - 为了实现对数据库版本进行管理,
SQLiteOpenHelper
类提供了两个重要的方法,分别是onCreate()**
和onUpgrade()
,前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。 - 当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法。
- onCreate()方法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。
- onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的。
- 假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2,并且在onUpgrade()方法里面实现表结构的更新。
- 当软件的版本升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后做出相应的表结构及数据更新。
- 用
SQLiteDatabase类
- Android提供了一个名为
SQLiteDatabase
的类(SQLiteOpenHelper
类中的getWritableDatabase()
和getReadableDatabase()
方法返回这个类的对象)。 SQLiteDatabase
类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。- SQLiteDatabase的学习,应该重点掌握execSQL()和rawQuery()方法。
- execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句;
- rawQuery()方法用于执行select语句。
- Android提供了一个名为
Java 部分
Java 设计模式
Retrofit:动态代理模式
RxJava:适配器模式
- Retorfit默认的DefaultCallAdapterFactory与Rxjava的适配器模式
Rxjava适配器模式
- Retorfit默认的DefaultCallAdapterFactory与Rxjava的适配器模式
MVP
登陆的 demo 步骤如下:
MainActivity 继承 LoginViewInterface
LoginViewInterface 包含 getView 和 setView 方法
LoginPresentImp 继承 LoginPresentInterface(包含登陆、注册方法)
LoginPresentInterface中包含 Login()、resister() 等方法
MainActivity中初始化:LoginPresentInterface mPresenter = new LoginPresentImp(this);
this 传递的是 LoginViewInterface 的实例
MainActivity 点击登陆按钮的时候调用 mPresenter.Login()
调用的是 LoginPresentImp 的 Login 方法
LoginPresentImp 的 Login 方法中拿着LoginViewInterface的实例给 activity 往来通信
数据结构部分
- 概念及分类: