【PoRE】Lab2: Android Programming

回到目录

内容总结

  • 先说些题外话,如各位所见,这是PoRE Labs系列最后一篇更新的博客,虽然是第二个Lab。究其原因,因为它难度不低,哪怕是这会我也不敢保证我写的这些学术垃圾——可能都算不上学术垃圾——这些垃圾代码是否完全正确。这个Lab本身难度也很大,尤其是对于绝大多数同学来说,本就是初学Java,然后三节课又速成Android Programming,网上的教程虽多,但平心而论质量参差不齐。关于这个Lab,其实也有学姐写过,这里我的博客仅供大家参考,不保证代码完全正确。
  • 那么回到这节课的内容,来总结一下课件上的内容。关于所有内容,如果有误,或不准确的,建议参考这里。此外,这里的总结内容仅针对考试内容,实际Lab完成中不限这些。

  App Component(应用组件):作为Android应用的基本构建块,一共分成四类:Activity、Service、Broadcast Receiver、Content Provider。对于前两个,请注意它们的生命周期(Lifecycle),在课件中会有精确的图展示。在Lab和本课程的开发中,大多聚焦于前三个组件。

  Intent and Intent Filter:Intent(意图),可以认为是不同组件之间用于传递消息的对象,包括但不限于启动活动(Activity)、启动服务、传递广播等。同样,意图也分为显式意图(Explicit Intent)和隐式意图(Implicit Intent)。而Intent Filter,则是作为组件接受隐式意图的一个判断条件,当一个隐式意图可以通过一个这个组件的预设的过滤器时,这个意图才会被接收。需要在Manifest中通过intent-filter元素声明过滤器。

  MAC & DAC:MAC(Mandatory Access Control,强制访问控制),由操作系统或系统管理员限制和控制访问权限。DAC(Discretionary Access Control,自主访问控制),由文件客体的所有者(及管理员)控制和管理访问权限。(部分参考

  Android Permission Model:分为Install-time Permissions(Android 5.1 or earlier)和Run-time Permissions(Android 6.0 and later)。四种保护级别(Protection Level),分别为:normal、dangerous、signature、signatureOrSystem。

Lab简介与参考

  • 在开始开发之前,需要搭建Android Studio以及其AVM的环境,当中可能会遇到诸多问题,如.gradle等等。关于这系列的问题,可以在站点的Q&A Site、或者是网上搜索得到。
  • 这个Lab一共分成4个Task。
  • Task 1: What’s in background

  事实证明,这个Task是最复杂的……需要我们做这些事:
(1) 注册一个广播接收器,在开机时自动启动,并且可以未经用户授权开启后台服务;
(2) 获得设备的GPS定位,每3秒打印出经纬度;
(3) MainActivity中需要询问Location权限。
  代码如下:
SecretBootReceiver.java

package com.smali.secretchallenge;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class SecretBootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //Call the service named SecretService
        context.startService(new Intent(context,SecretService.class));
    }
}

  这个应该没有什么难度:就是简单地实现BroadcastReceiver这一接口,然后使用Intent启动服务。

SecretService.java

package com.smali.secretchallenge;

import android.Manifest;
import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.ActivityCompat;
import android.widget.Toast;

import java.util.List;

public class SecretService extends IntentService {
    public SecretService() {
        super("SecretService");
    }
    private Intent intent_copy;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        intent_copy=intent;
        onHandleIntent(intent);
        return START_REDELIVER_INTENT;
    }

    @Override
    public IBinder onBind(Intent intent) {
        //Not allowed.
        return null;
    }

    @Override
    public void onHandleIntent(Intent intent) {
        LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "No Permission!", Toast.LENGTH_LONG).show();
            onDestroy();
        }
        List<String> providerList = locationManager.getProviders(true);
        String provider;
        if (providerList.contains(locationManager.GPS_PROVIDER)) {
            provider=locationManager.GPS_PROVIDER;
        }
        else if (providerList.contains(locationManager.NETWORK_PROVIDER)) {
            provider=locationManager.NETWORK_PROVIDER;
        }
        else {
            Toast.makeText(this,"No Location Provider :)",Toast.LENGTH_LONG).show();
            return;
        }
        locationManager.requestLocationUpdates(provider, 3000, 10, new LocationListener() {
            @Override
            public void onLocationChanged(Location location) { }
            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) { }
            @Override
            public void onProviderEnabled(String provider) { }
            @Override
            public void onProviderDisabled(String provider) { }
            });
        Location location = locationManager.getLastKnownLocation(provider);
        if (location==null) {
            Toast.makeText(this,"location is null",Toast.LENGTH_LONG).show();
        }
        String accuracy = Float.toString(location.getAccuracy());
        String altitude = Double.toString(location.getAltitude());
        String latitude = Double.toString(location.getLatitude());
        String text = "getAccuracy:" + accuracy + "\n" + "getLatitude:" + latitude + "\n" + "getAltitude:" + altitude;
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
        try {
            wait(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        onDestroy();
    }

    @Override
    public void onDestroy() {
        try {
            wait(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        startService(new Intent(this,SecretService.class));
        super.onDestroy();
    }
}

这里围绕代码说明几点:
  (1) 最致命的一个地方是按照代码预设,确实是三秒更新一次经纬度,但Toast的打印的3秒效果没有实现(就是这个Toast会一直显示,而不是隐藏后又显示,这里当初是因为时间原因没有实现完)。也就是说,里面的几句wait(3000);似乎并没有什么用。
  (2) 这里实现的Service是一个变种IntentService,关于具体细节可以见这里。作为特别的IntentService,需要特别实现的方法就是onHandleIntent()。由于IntentService在处理完所有请求后会结束,因此我这里的预期是结束后再重新启动本Service,没有测试过这是不是导致Toast效果一直不理想的原因。
  (3) 这里关于providerList等的使用,都是参考网上的博客。随后大部分内容应该还比较正常……

MainActivity.java

// Task 1 TODO: How to dynamic request some sensitive permission? And how to monitor the BOOT_COMPLETED action to start a secret service to toast?
// Launch a request for relevant permission.
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
	//No location permissions.
	requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION },0);
}

  这部分应该还比较正常,参考的是这里

  • Task 2: Read the file and change the text in the screen

  这个Task主要聚焦于文件的读,以及对应UI的改变。这个应该比较简单,查清对应的API就可以了。

//private String FILENAME = "hello_file";
//This is the String you get from the EditText view.
final String input = input_edit.getText().toString();
//Task 2 TODO:How to read a file from the internal storage? And how to change the text that showed in the MainActivity?
byte[] byteBuffer = new byte[99];
String text = "";
try {
	FileInputStream fis = openFileInput(FILENAME);
	fis.read(byteBuffer);
	text = new String(byteBuffer,"utf-8");
	fis.close();
} catch (Exception e) {
	e.printStackTrace();
}
tv.setText(text);
  • Task 3: Change UI in the thread

  也如助教的提示,这里需要使用Handler和Looper来处理。关于这两个类,以及Message的使用,可以在网上找到不少资源,这里不具体展开了。

//private Handler handler1;

//在Button.onClickListener内
//The App will crash if you click the button. So edit this class.
// Task 3 TODO: In child thread how to make Dialog in main thread?
// 传递input
Message msg = Message.obtain();
msg.what = 1;
msg.obj = input;
handler1.sendMessage(msg);

//在Button.onClickListener外
new Thread(new Runnable() {
@Override
	public void run() {
		// Make a dialog rather than edit the text in main UI
		// tv.setText(input);

		//Receive the message
		Looper.prepare();
		handler1 = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
				builder.setTitle(msg.obj.toString());
				builder.show();
			}
		};
		Looper.loop();
}).start();

这里作出几点说明:
  (1) 这里的话有两个匿名内部类,关于内部类及抽象类的方法重写(Override)概念如有问题,可以参照Java Programming或百度。
  (2) 这里使用了AlertDialog.Builder类,关于Dialog类可参考这里,作为AlertDialog的使用可参考这里
  (3) 这里特别记录一个错误:如果在Builder构造函数的传参传入getApplicationContext()会报错,而像上面这样传入MainActivity.this则会安然无恙,这里是参考了StackOverflow的一则Q&A。(很可惜链接找不到了)

  • Task 4: How to call that method?

  这个Task可能是最简单的了,它甚至与Android Programming本身无关——它涉及Java的反射(Reflection)机制。先贴代码。

// Task 4 TODO: How to get the private variable of PoRELab class? And How to invoke the private method?
//PoRELab.publicMethod(input);
try {
	PoRELab poreObject = new PoRELab();
	Method method = PoRELab.class.getDeclaredMethod("privateMethod", String.class, String.class);
	Field field = PoRELab.class.getDeclaredField("curStr");
	method.setAccessible(true);
	field.setAccessible(true);
	String fieldValue = (String)field.get(poreObject);
	method.invoke(poreObject,input,fieldValue);
} catch (Exception e) {
	e.printStackTrace();
}

  这里的话需要特别注意的是method.setAccessible(true);(field同理)这一行代码。由于这里的反射机制推出的是私有方法,如果不加这两行代码的话会提示找不到字段和方法。

写在最后

  • 这个Lab,如果我没有记错的话,是从当时做直到学期结束,张老师及助教和同学们交流时,大家都反馈是“最难的”一个Lab。想象一下,一个没有Java基础的同学,一个礼拜速成Java,一个礼拜又速成Android开发,这确实有一些挑战性。抛开这个Lab的实用性不谈,本身的难度要求比较大,就要求学习能力要强,可能我不太适合……
  • 感谢在做这个Lab时看到的,无论是有用的还是没用的那些博客,很可惜在目前总结的阶段已经找不到绝大多数博客了,只能找到一些偏官方性的文档了。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Title: Android Programming: The Big Nerd Ranch Guide, 3rd Edition Author: Bill Phillips, Chris Stewart Length: 624 pages Edition: 3 Language: English Publisher: Big Nerd Ranch Guides Publication Date: 2017-02-09 ISBN-10: 0134706056 ISBN-13: 9780134706054 Table of Contents Chapter 1. Your First Android Application Chapter 2. Android and Model-View-Controller Chapter 3. The Activity Lifecycle Chapter 4. Debugging Android Apps Chapter 5. Your Second Activity Chapter 6. Android SDK Versions and Compatibility Chapter 7. UI Fragments and the Fragment Manager Chapter 8. Displaying Lists with RecyclerView Chapter 9. Creating User Interfaces with Layouts and Widgets Chapter 10. Using Fragment Arguments Chapter 11. Using ViewPager Chapter 12. Dialogs Chapter 13. The Toolbar Chapter 14. SQLite Databases Chapter 15. Implicit Intents Chapter 16. Taking Pictures with Intents Chapter 17. Two-Pane Master-Detail Interfaces Chapter 18. Localization Chapter 19. Accessibility Chapter 20. Data Binding and MVVM Chapter 21. Unit Testing and Audio Playback Chapter 22. Styles and Themes Chapter 23. XML Drawables Chapter 24. More About Intents and Tasks Chapter 25. HTTP and Background Tasks Chapter 26. Loopers, Handlers, and HandlerThread Chapter 27. Search Chapter 28. Background Services Chapter 29. Broadcast Intents Chapter 30. Browsing the Web and WebView Chapter 31. Custom Views and Touch Events Chapter 32. Property Animation Chapter 33. Locations and Play Services Chapter 34. Maps Chapter 35. Material Design Chapter 36. Afterword
When Android first arrived in 2008, it was almost seen as a poor relation to the much more stylish iOS on Apple iPhone. But, quite quickly, through diverse handset offers that struck a chord with both the practical price-conscious as well as the fashion-conscious and tech-hungry consumers, Android user numbers exploded. Now, after seven major releases, the annual sales of Android devices is increasing almost every year. For many, myself included, developing Android apps is the most rewarding thing (apart from our friends and family) in the world. Quickly putting together a prototype of an idea, refining it, and then deciding to run with it as well wiring it up into a fully-fledged app is an exciting and rewarding process. Any programming can be fun, and I have been programming all my life, but creating for Android is somehow extraordinarily rewarding. Defining exactly why this is so is quite difficult. Perhaps it is the fact that the platform is free and open. You can distribute your apps without requiring the permission of a big controlling corporation—nobody can stop you. And at the same time, you have the well-established, corporate-controlled mass markets such as Amazon App Store, Google Play, Samsung Galaxy Apps, as well as other smaller marketplaces. More likely, the reason developing for Android gives such a buzz is the nature of the devices. They are deeply personal. You can create apps that actually interact with people's lives. You can educate, entertain, organize them, and so on. But it is there in their pocket ready to serve them in the home, workplace, or on holiday. Everyone uses them, from infants to seniors.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值