Android开发入门小知识

1.获取全局Context

通过Context, 可以启动服务, Activity, 通知, 创建操控内容提供器(ContentProvider)的对象SQLiteOpenHelper等. 在Activity中可以很容易获取Context, 但是在自己封装的工具类中, 就不能像Activity中使用继承至父类方法getContext来获取. 通用的方法是获取Application的Context.

首先新建继承自Application的类MyApplication.

public class MyApplication extends Application {

    private static Context context;
    @Override
    public void onCreate() {
        super.onCreate();

        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }
}

然后再AndroidManifest中使用新建的MyApplication.

<application
    android:name=".MyApplication"
    ...
</application>

这样在封装的工具类中就可以使用MyApplication的getContext方法, 轻易获取全局Context.

public class HttpUtil {

    public static void sendHttpRequest(final String address, final HttpCallbackListener listener){

        if (!isNetworkAvailable()){
            Toast.makeText(MyApplication.getContext(), "noNet", Toast.LENGTH_SHORT).show();
            return;
        }
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        HttpURLConnection connection = null;
                        try {
                            URL url = new URL(address);
                            connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            connection.setConnectTimeout(8000);
                            connection.setReadTimeout(8000);
                            connection.setDoInput(true);
                            connection.setDoOutput(true);
                            InputStream in = connection.getInputStream();

                            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                            StringBuilder response = new StringBuilder();
                            String line;
                            while ((line = reader.readLine()) != null){
                                response.append(line);
                            }

                            if (listener != null){
                                listener.onFinish(response.toString());
                            }
                        }catch (Exception e){
                            if (listener != null){
                                listener.onError(e);
                            }
                        }finally {
                            if (connection != null){
                                connection.disconnect();
                            }
                        }
                    }
                }
        ).start();
    }

    private static boolean isNetworkAvailable(){
        return true;
    }
}

2.实现自己的Log日志工具类

开发过程中, 我们常常借助打印出的详细日志来调试程序, 可以帮助自己理顺开发逻辑思路. 但是, 如果发布之后就没有必要再次打印, 不然会在后台消耗大量的手机资源, 给用户造成App是耗电大户的假象. 解决方式是可以自定义日志工具类, 通过参数控制日志是否打印.

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 final 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);
        }
    }
}

调试状态下, 把level设置成VERBOSE的级别, 打印出所有日志; 发布时, 将level设置成NOTHING级别, 不打印日志.

3.后台定时执行

很多时候, 作为前端的App需要和服务端定时通信, 比如行情数据的刷新、报价的定时提醒等. Android中通过AlarmManager可以实现.

public class LongRunningService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("LongRunningService", "run: ");
            }
        }).start();

        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
        int dur = 5 * 1000;
        long triggerAtTime = SystemClock.elapsedRealtime() + dur;
        Intent intent1 = new Intent(this, LongRunningService.class);

        PendingIntent pi = PendingIntent.getService(this, 0, intent1, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);

        return super.onStartCommand(intent, flags, startId);
    }
}

通过SystemClock.elapsedRealtime()可以获取自从Android开机累计的时间. 通过System.currentTimeMillis()可以获取自从1970年1月1日0点( UNIX和C语言诞生)开始累计的时间. manager的set方法接受3个参数, 第一个参数表示启用的时间起点: ELAPSED_REALTIME_WAKEUP表示使用开机累计的时间, RTC表示1970年累计的时间. 第二个参数表示触发的时间点. 第三个参数表示要启动的PendingIntent.

通过以上设置, 就能在启用LongRunningService后, 每隔5s执行Thread中的run方法.

4.Doze模式

为了限制Android后台服务过多造成的耗电过快问题, Android6.0推出了Doze模式. 在Doze模式下, 系统会对CPU、网络、Alarm活动等进行限制.

Doze模式

从图上可以看出, 手机退出Doze模式间隔的时间逐渐延长, 因为这时可以认为用户与手机交互的需要逐渐降低. 如果在Doze模式下要求Alarm强制执行, 可以使用AlarmManager的setAndAllowWhileIdle或者setExactAndAllowWhileIdle方法.

5.0 使用Intent传递对象

我们知道通过Intent可以传递String, int等基本数据类型, 但是如果是class对象, 该如何传递呢? 有Serializable和Parcelable两种方式.

<1>Serializable

public class Person implements Serializable {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

然后就可以用Intent来传递:

Person person = new Person();
person.setAge(18);
person.setName("xiaoming");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("person", person);
startActivity(intent);

在SecondActivity中的获取方式:

Person person = (Person) intent.getSerializableExtra("person");
Log.d(TAG, "onCreate: " + person.getName() + ", " + person.getAge());

<2>Parcelable
Android推出Parcelable是为了解决使用Serializable存取对象效率过慢的问题, 将存取对象的地方从硬盘中提升到内存中进行. 它也通过接口的方式来实现.

public class Person implements Parcelable {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(age);
    }

    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel parcel) {

            Person person = new Person();
            person.name = parcel.readString();
            person.age = parcel.readInt();
            return person;
        }

        @Override
        public Person[] newArray(int i) {
            Log.d(TAG, "newArray11111: " + i);
            return new Person[i];
        }
    };
}

传递方式和Serializable方式一样, 获取方式有所区别.

Person person = (Person) intent.getParcelableExtra("person");
Log.d(TAG, "onCreate: " + person.getName() + ", " + person.getAge());

6. 多窗口模式
在Android7.0下, 长按底部控制栏最右边的OverView按钮, 就会自动进入多窗口模式.

多窗口模式

在项目MaterialTest3的MainActivity中, 我们在页面的生命周期中添加打印消息, 来观察多窗口模式下页面的生命周期.

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MaterialTest3";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();

        Log.d(TAG, "onStart: ");
    }

    @Override
    protected void onResume() {
        super.onResume();

        Log.d(TAG, "onResume: ");
    }

    @Override
    protected void onPause() {
        super.onPause();

        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();

        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        Log.d(TAG, "onDestroy: ");
    }

    @Override
    protected void onRestart() {
        super.onRestart();

        Log.d(TAG, "onRestart: ");
    }
}

观察打印日志:

D/MaterialTest3: onPause: 
D/MaterialTest3: onStop: 
D/MaterialTest3: onDestroy: 
D/MaterialTest3: onCreate: 
D/MaterialTest3: onStart: 
D/MaterialTest3: onResume: 
D/MaterialTest3: onPause: 

可以看到界面实际上是重新销毁, 二次创建的过程.

通过设置MainActivity的属性来禁用多窗口模式:

<activity android:name=".MainActivity"
   android:resizeableActivity="false"
   android:screenOrientation="portrait">
   <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

7.Lambda表达式

Android是基于Java的语言, Java 8.0的发布也给Android的开发带来了便利. Java8.0支持Lamda表达式、streamAPI、接口默认实现等. streamAPI和接口默认实现只支持Android 7.0以上的系统, 而Lamda表达式最低支持Android 2.3, 值得学习一下.

首先需要在app的build.gradle文件中添加Java8的支持.

defaultConfig {
    jackOptions.enabled = true
   ...
}
compileOptions{
    sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8
    targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8
}

然后我们可以很方便的实现只有一个方法的接口. 首先定义接口:

public interface MyInterface_Lambda {
    public String speak(String a, String b);
}

然后可以如下实现和调用接口:

MyInterface_Lambda myInter = (a, b) -> {
            return a + b;
        };
myInter.speak("Hello ", "world!");

其中Lambda表达式可以自动推导出参数a, b 为String类型. 是不是很方便?

喜欢和关注都是对我的鼓励和支持~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359

推荐阅读更多精彩内容