《第一行代码》第2版思维导图及所有学习笔记目录
全局获取Context
新建一个自己Application,如下:
1 2 3 4 5 6 7 8 9 10 11 12
| public class MyApplication extends Application { private static Context context; @Override public void onCreate() { context = getApplicationContext(); } public static Context getContext() { return context; } }
|
指定程序启动时初始化的Application,如下:
1 2 3 4 5
| <application android:name="com.example.networknest.MyApplication" //完整的包名 ...> ... </application>
|
需要Context时而没有,即可调用如下:
1
| Toast.makeText(MyApplication.getContext(),...).show();
|
不过LItePal要正常工作也需要配置起自己的Application,如下:
1
| android:name="org.litepal.LitePalApplication"
|
其也是为了能在内部自动获取Context,遇到这种冲突,类似如下方式解决:
1 2 3 4 5 6 7 8 9 10
| public class MyApplication extends Application { private static Context context; @Override public void onCreate() { context = getApplicationContext(); LitePalApplication.initialize(context); } ... }
|
使用Intent传递对象
Serializable方式
将对象实现Serializable接口,让对象成为序列化可存储传输状态,如下:
1 2 3 4 5 6 7 8
| # 对象实现接口 public class Person implements Serializable {}
# 传输 intent.putExtra("person_data",person);
# 接收 Person person = (Person)getIntent().getSerializableExtra("person_data");
|
Parcelable方式
推荐此方式,效率更高
Parcelable方式是将对象分解为Intent支持的数据类型,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| # 对象实现接口 public class Person implements Parcelable { private String name; ... @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } public static final Parcelable.Creator<Person> CREATOR = new Parecelable.Creator<Person>() { @Override public Person createFromParcel(Parcel source) { Person person = new Person(); person.name = source.readString(); person.age = source.readInt(); return person; } @Override public Person[] newArray(int size) { return new Person[size]; } }; }
# 传输 intent.putExtra("person_data",person);
# 接收 Person person = (Person)getIntent().getParcelableExtra("person_data");
|
定制日志工具
为了方便控制开关日志,一般会定义一个日志工具类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class LogUtil { public static final int VERBOSE = 1; public static final int DEBUG = 2; public static final int INFO = 3; public static final int WARN = 4; public static final int ERROR = 5; public static final int NOTHING = 6; public static int level = VERBOSE; public static void v(String tag, String msg) { if (level <= VERBOSE) { Log.v(tag, msg); } } public static void d(String tag, String msg) { if (level <= DEBUG) { Log.d(tag, msg); } } public static void i(String tag, String msg) { if (level <= INFO) { Log.i(tag, msg); } } public static void w(String tag, String msg) { if (level <= WARN) { Log.w(tag, msg); } } public static void e(String tag, String msg) { if (level <= ERROR) { Log.e(tag, msg); } } }
# 调用 LogUtil.v() ...
# 关闭所有日志 LogUtil.level = NOTHING;
|
创建定时任务
Alarm机制
简单用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); long triggerAtTime = SystemClock.elapsedRealtime() + 10*1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKE_UP, triggerAtTime, pendingIntent);
or
long triggerAtTime = System.currentTimeMillis() + 10*1000;
manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pendingIntent); ...
|
一个后台定时服务示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class LongRunningService extends Service { @Override onBind(){}
@Override public int onStartCommand(Intent intent , int flags , int startId) { new Thread(new Runnable() { public void run(){ } }).start(); AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); long triggerAtTime = SystemClock.elapsedRealtime() + 60*60*1000; Intent i = new Intent(this, LongRunningService.class); PenddingIntent pi = PendingIntent.getService(this, 0, i, 0); manager.set(AlarmManager.ELAPSED_REALTIME_WAKE_UP, triggerAtTime, pendingIntent); return super.onStartCommand(intent, flags, startId); } }
|
需要定时服务时启动服务:
1 2
| Intent intent = new Intent(this, LongRunningService.class); context.startService(intent);
|
不过为了功耗,Android限制减少了cpu唤醒次数,set()可能不十分准确,若准确性要求高,可使用setExact()方法。
Doze模式
Android6.0及以上为了省电新加入了Doze模式,满足未插电、屏幕关闭等一段时间将进入doze模式,对cpu、网络、Alarm等进行限制,如下为Android7.0进入Doze模式的两个阶段:
阶段一:
阶段二:
如若想Alarm在Doze模式也能正常执行,则需要使用AlarmManager的如下两个方法:
1 2
| setAndAllowWhileIdle() setExactAndAllowWhileIdle()
|
区别同上set()与setExact()
多窗口模式编程
进入多窗口模式活动会重建,可以通过如下方式改变:
1 2 3 4 5 6
| <activity ... android:configChanges="orientation|keyboardHidden|screenSize|screenLayout"> // 多窗口、横竖屏切换,活动皆不会被重建 ... </activity>
|
屏幕的变化将通知到Acitvity的onConfigurationChanged()方法,可以重写此方法进行逻辑处理。
禁用多窗口模式:
1 2
| # <application>或<activity>标签加入 android:resizeableActivity="false" // 默认为true,支持
|
不过targetSDKVersion低于24的,上述属性将不会生效,这就得通过另一种方案:制定不支持横竖屏切换,因为android规定24以下不允许横竖屏切换的亦不知多窗口,如下:
1
| android:screenOrientation=["portrait"|"landscape"]
|
Lambda表达式
使用Java8新特性,需先在app/build.gradle添加如下配置:
1 2 3 4 5 6 7 8 9
| defaultConfig { ... jackOptions.enabled = true }
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
|
只有一个待实现方法的接口,都可使用Lambda表达式,如下:
1 2 3 4 5 6 7 8
| new Thread(new Runnable() { public void run(){} }).start();
# Lambda 方式 new Thread(() -> { }).start();
|
带参数的书写方式:
1 2
| (String a, int b) -> { }
|