Skip to main content

Android Basic Notes

Android Framework

Dalvik virtual machine

  1. register-based machine 基于寄存器(不写入内存)
  2. minimizing instruction dispatch and memory accesses 最小化指令分配黑内存访问
  3. giving more efficient instruction stream(a lot more semantic content) 提供更加高效的指令流

Basic Building Blocks

  1. Activity(Managed by activity stack)
  2. Service(Running in the background;with no UI)
  3. Broadcast Receiver(Can invoke(调用) activity;with no UI)
  4. Content Provider(accessing and managing application data)

Android Studio

Plugins

Code generator

  • Constructor
  • getter/setter
  • ViewHolder
  • Parcelable Implementation
  • GsonFormat : 根据 JSONObject 生成相应类

API Conventions

Manager Service

  • PreferenceManager.getDefaultSharedPreferences
  • LocalBroadcastManager.getInstance

Activity

Base Activity

查看当前界面属于哪个 Activity,自定义 Activity 继承 BaseActivity

public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
}
}

Activity Collector

在所有 Activity 的 onCreate 方法调用静态的 addActivity 方法,onDestroy 方法调用静态的 removeActivity 方法。

  • ActivityCollector.addActivity(this);
  • ActivityCollector.removeActivity(this);
public class ActivityCollector {

public static List<Activity> activities = new ArrayList<Activity>();

public static void addActivity(Activity activity) {
activities.add(activity);

}

public static void removeActivity(Activity activity) {
activities.remove(activity);

}

public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}

Start Activity

为每个 Activity 添加静态的 actionStart 方法,供其他 Activity 启用此 Activity

public static void actionStart(Context context, String data1, String data2) {
Intent intent = new Intent(context, thisActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);
context.startActivity(intent);
}

UI Design

N activities can respond to a particular intent:

Android will pop(弹出) up a little dialogue list(对话框) to user showing application icon defining the intent

当有多个活动可相应某个特定意图时,系统将会弹出对话框提示用户选择一个应用的活动或者设定默认值(default)

e.g web browsers

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

Layout

Basic

  • android:layout_weight 自适配布局
  • android:SingleLine 单行显示模式
  • android:ellipsize="end" 文字过多时缩略方式

Table Layout

  • <TableLayout android:stretchColumns="1"> 拉伸第 2 列
  • android:layout_span="2" 占 2 列

Custom Layout

LayoutInflater 作用是将 layout 的 xml 布局文件实例化为 View 类对象。

View view = LayoutInflater.from(context).inflate(R.layout.title, this/null);

Component

View(ViewGroup): e.g button、textbox(文本框)、checkbox(复选框)

Custom Component

custom XML

title.xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_bg"
>
<button
android:id="@+id/title_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dip"
android:background="@drawable/back_bg"
android:text="Back"
android:textColor="#fff"
/>
<TextView
android:id="@+id/title_text"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:text="Title Text"
android:textColor="#fff"
android:textSize="24sp"
/>
<button
android:id="@+id/title_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dip"
android:background="@drawable/edit_bg"
android:text="Edit"
android:textColor="#fff"
/>
</LinearLayout>

custom class

public class TitleLayout extends LinearLayout {
public TitleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title, this);

//Register button click Listener
titleBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity) getContext()).finish();
}
});
titleEdit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), "You clicked Edit button", Toast.LENGTH_SHORT).show();
}
});

//other awesome things
//like material design ripple effect
//animations and music
}

}

AlertDialog

//builder pattern
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);

dialogBuilder.setTitle("Warning");
dialogBuilder.setMessage("You are forced to be offline. Please try to login again.");
dialogBuilder.setCancelable(false);
dialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll(); // 销毁所有活动
Intent intent = new Intent(context, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); // 重新启动LoginActivity
}
});

AlertDialog alertDialog = dialogBuilder.create();

// 需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

alertDialog.show();

List View

ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_list_item_1, data
);
listView.setAdapter(adapter);
Custom List View Layout
  • Custom class
  • Custom Sub Xml(单项)
  • Custom ArrayAdapter
    • 重写构造函数
    • 重写 getView 方法
      • 重用 convertView 提升性能
      • ViewHolder 提升性能

//内部类,其中字段与自定义class的字段一致
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
public FruitAdapter(
Context context,
int textViewResourceId,
List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

Fruit fruit = getItem(position); // 获取当前项的Fruit实例
View view;
ViewHolder viewHolder;

//大幅提升性能
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);

viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder); // 将ViewHolder存储在View中

} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
}

ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
Custom List View Listener
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(
AdapterView<?> parent,
View view,
int position,
long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
}
});

adapter.notifyDataSetChanged(); // 当有新消息时,刷新ListView中的显示
msgListView.setSelection(msgList.size()); // 将ListView定位到最后一行

Drawable

修改特定组件的背景颜色

Resources myColor = getBaseContext().getResources();
// getBaseContext()获得基础Context
// getResources()获得资源
Drawable color_M = myColor.getDrawable(R.color. lightGreen);
// 由资源 myColor来获得Drawable
// R.color.lightGreen是颜色值的ID引用
text.setBackgroundDrawable(color_M);
//设置背景

Fragment

android.app.Fragment

Basic Fragment

Activity XML

<fragment android:id="@+id/right_fragment" <!-- custom fragment class -->
android:name="com.example.fragmentTest.RightFragment"
android:layout_width="0dp" android:layout_height="match_parent"
android:layout_weight="1" /></fragment
>

Create View in Fragment

@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}

Add Fragment in Activity

AnotherRightFragment fragment = new AnotherRightFragment();
FragmentTransaction transaction = getFragmentManager.beginTransaction();

//容器的 id 和待添加的碎片实例
transaction.replace(R.id.right_layout, fragment);
//模拟返回栈
transaction.addToBackStack(null);
transaction.commit();

Transfer Information

In Activity

getFragmentManager().findFragmentById(R.id.right_fragment);

In Fragment

MainActivity activity = (MainActivity) getActivity();

Runtime Loop

Basic Override Function

  • onAttach() 当碎片和活动建立关联的时候调用。
  • onCreateView() 为碎片创建视图(加载布局)时调用。
  • onActivityCreated() 确保与碎片相关联的活动一定已经创建完毕的时候调用。
  • onDestroyView() 当与碎片关联的视图被移除的时候调用。
  • onDetach() 当碎片和活动解除关联的时候调用。

Broadcast

  • Normal Broadcasts : async
  • Ordered Broadcasts : sync

Register Receiver

In Activity

//Custom BroadcastReceiver,Override onReceive methods
//intentFilter : action
//前为响应后的行为,后为响应何种广播
registerReceiver(networkChangeReceiver, intentFilter);

//in onDestroy
unregisterReceiver();

In AndroidManifest,xml

<!-- custom receiver class -->
<receiver android:name=".MyBroadcastReceiver">
<!-- receiver priority -->
<intent-filter android:priority="100">
<!-- custom broadcast -->
<action android:name="com.example.broadcastTest. MY_BROADCAST" />
</intent-filter>
</receiver>

Custom Broadcast

Normal Broadcast

intent intent = new Intent("com.example.broadcastTest.MY_BROADCAST");
sendBroadcast(intent);

Ordered Broadcast

intent intent = new Intent("com.example.broadcastTest.MY_BROADCAST");
sendOrderedBroadcast(intent, null);

Local Broadcast

// 获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.sendBroadcast(intent); // 发送本地广播

Local Receiver

localBroadcastManager.registerReceiver(CustomReceiver, intentFilter);
localBroadcastManager.unregisterReceiver(CustomReceiver);

Data Store

Files Store

/data/data/<packageName>/files/

Write

String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

Read

FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

return content.toString();

Shared Preferences

/data/data/<packageName>/shared_preferences/

Write

//get Editor
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
//store date
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
//commit
editor.commit();
editor.clear();

clear pref file content

Read

SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
//second argument - default value if target key don't exists
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean married = pref.getBoolean("married", false);

DataBase

/data/data/<package name>/databases/

SQLite Open Helper

@Override
onCreate()
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL("alter table Book add column category_id integer");
default:
}
}

Custom 实现创建、升级数据库的逻辑 构建出 SQLiteOpenHelper 的实例后,再调用getReadableDatabase()getWritableDatabase()方法创建数据库

establish table
create table Book (
id integer primary key autoIncrement,
author text,
price real,
pages integer,
name text
)

Basic Operator

  • 创建一个新的数据库并返回一个 SQLiteDatabase 对象
Context.createDatabase(String name,int version ,int mode,CursorFactory factory);
  • 删除数据库
this.deleteDatabase("myDatabase.db");
  • 打开数据库
SQLiteDatabase my_DataBase =
this.openOrCreateDatabase("myDateBase.db",MODE_PRIVATE , null);
my_DataBase.close();
  • 非查询 SQL 指令
//创建一个名为"test"并带两个参数的表
my_DataBase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY,
someNumber INTEGER);");
//在数据库中插入一个元组
my_DataBase.execSQL("INSERT INTO test (_id,someNumber) values(1,8);");

SQLiteDatabase db = dbHelper.getWritableDatabase();

//INSERT
ContentValues values = new ContentValues();
// 开始组装第一条数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); // 插入第一条数据
values.clear();
// 开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values); // 插入第二条数据

//update
contentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] { "The DaVinci Code" });

//delete
db.delete("Book", "pages > ?", new String[] { "500" });

//删除表
my_DataBase.execSQL("DROP TABLE test");
  • 查询 SQL 指令-游标 Cursors
    • query()
方法参数对应 SQL 部分描述
tablefrom tableName指定查询的表名
columnsselect column1, column2指定查询的列名
selectionwhere column = value指定 where 的约束条件
selectionArgs-为 where 中的占位符提供具体的值
groupBygroup by column指定需要 group by 的列
havinghaving column = value对 group by 后的结果进一步约束
orderByorder by column1, column2指定查询结果的排序方式
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 查询Book表中所有的数据
Cursor cursor = db.query("Book", null, null, null, null, null, null);
// 遍历Cursor对象,取出数据
//cursor.moveToFirst()
//cursor.moveToNext()
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
//为了创建一个Cursor(游标),必须执行一个查询,要么通过SQL使用rawQuery()方法
//或是更精心设计的方法,像query()方法
Cursor cur = my_DataBase.rawQuery("SELECT * FORM test", null);

if(cur!=null) {//游标不为空
//返回给定名称的列的基于0开始的index,如果该属性列不存在则返回-1
//通过它们的index来检索属性值
int numColumn=cur.getColumnIndex("someNumber");

if(cur.moveToFirst()) {
//cur.moveToFirst()让游标指向第一行,如果游标指向第一行,则返回true
do {
int num=cur.getInt(numColumn);//获得当前行该属性的值
/*Cursor提供了不同的方法来回索不同的数据类型
例如getInt(int index)/getString(int index)等等*/
/*做一些事情*/
} while (cur.moveToNext());
/*游标移动到下一行,如果游标已经通过了结果集中的最后,
即没有行可以移动时,则返回false*/
//其他可能移动的是 previous() 和first()方法
}
}

Transaction

  • SQLiteDatabase 的beginTransaction()方法
  • 调用setTransactionSuccessful()表示事务已经执行成功
  • finally 代码块中调用endTransaction()来结束事务

Content Provider

Read Other App Content

Uri uri = Uri.parse("content://com.example.app.provider/table1");
getContentResolver().query/insert/delete/update();
  • ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
  • ContactsContract.CommonDataKinds.Phone.NUMBER

Provide App Content

With ContentProvider:

public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
}
UriMatcher.addURI(uri, customNumber)/.match(uri)
  • 为传入 URI 指定自定义常量作为代号

Service

Handler

//在主线程重写handleMessage,更新UI
new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
// 在这里可以进行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
}

//在后台执行子线程
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); // 将Message对象发送出去
}
}).start();

Async Task

AsyncTask 中的几个方法才能完成对任务的定制。经常需要去重写的方法 有以下四个:

  1. onPreExecute() 这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作, 比如显示一个进度条对话框等。
  2. doInBackground(Params...) 这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。 任务一旦完成就可以通过 return 语句来将任务的执行结果返回,如果 AsyncTask 的 第三个泛型参数指定的是 Void,就可以不返回任务执行结果。注意,在这个方法中是不 可以进行 UI 操作的,如果需要更新 UI 元素,比如说反馈当前任务的执行进度, 可以调用 publishProgress(Progress...)方法来完成。
  3. onProgressUpdate(Progress...) 当在后台任务中调用了 publishProgress(Progress...)方法后,这个方法就会很快被调用, 方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对 UI 进行操作, 利用参数中的数值就可以对界面元素进行相应地更新。
  4. onPostExecute(Result) 当后台任务执行完毕并通过 return 语句进行返回时,这个方法就很快会被调用。 返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些 UI 操作, 比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show(); // 显示进度对话框
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload(); // 这是一个虚构的方法
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
// 在这里更新下载进度
progressDialog.setMessage("Downloaded " + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss(); // 关闭进度对话框
// 在这里提示下载结果
if (result) {
Toast.makeText(context, "Download succeeded",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, " Download failed",
Toast.LENGTH_SHORT).show();
}
}
}

Basic Service

//in AndroidManifest.xml <service android:name=".MyService"> </service>
stopSelf()
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}

IBinder

  • In Custom Service class
private DownloadBinder mBinder = new DownloadBinder();

class DownloadBinder extends Binder {
public void startDownload() {
Log.d("MyService", "startDownload executed");
}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return 0;
}
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
  • In Activity class
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;

@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}

ForeGround Service

  • In service onCreate
Notification notification = new Notification(R.drawable.ic_launcher,
"Notification comes", System. currentTimeMills());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
notification.setLatestEventInfo(this, "This is title", "This is content", pendingIntent);
startForeground(1, notification);

Intent Service

public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); // 调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent) {
// 打印当前线程的id
Log.d("MyIntentService", "Thread id is " + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy executed");
}
}

Alarm Service

结合 BroadcastReceiver 可以实现定时任务

AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
  • Service 延时发出广播
  • BroadcastReceiver 接受广播后再次启动 Service
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("LongRunningService", "executed at " + new Date().toString());
}
}).start();

AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour = 60 * 60 * 1000; // 这是一小时的毫秒数
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;

//关键:在服务里发送广播
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);

manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);

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

Media

Notification

NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

Notification notification = new Notification(R.drawable.ic_launcher, "This is
ticker text", System.currentTimeMills());

Intent intent = new Intent(this, NotificationActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

notification.setLatestEventInfo(this, "This is content title", "This is
content text", pi);

manager.notify(1, notification);
//在被启动Activity manager.cancel(1);

SMS

Audio

Music

NetWork

Networked Apps

  1. Network latency(网络延迟)——UI thread separated from data loading thread
  2. Battery drain(电池耗尽)
  3. Intermittent service(中断服务)

WebView

<uses-permission android:name="android.permission.INTERNET" />

Three Steps

webView.getSettings().setJavaScriptEnabled(true);

webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); // 根据传入的参数再去加载新的网页
return true; // 表示当前WebView可以处理打开新网页的请求,不用借助系统浏览器
}
});

webView.loadUrl("http://www.github.com");

HTTP URL Connection

HTTP Client

XML

Pull

HttpClient httpClient = new DefaultHttpClient();

// 指定访问的服务器地址是电脑本机
HttpGet httpGet = new HttpGet("http://10.0.2.2/get_data.xml");
HttpResponse httpResponse = httpClient.execute(httpGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity,"utf-8");

//XML Pull 方式解析
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();

String id = "";
String name = "";
String version = "";

while (eventType != XmlPullParser.END_DOCUMENT) {

String nodeName = xmlPullParser.getName();

switch (eventType) {
// 开始解析某个结点
case XmlPullParser.START_TAG: {
if ("id".equals(nodeName)) {
id = xmlPullParser.nextText();
} else if ("name".equals(nodeName)) {
name = xmlPullParser.nextText();
} else if ("version".equals(nodeName)) {
version = xmlPullParser.nextText();
}
break;
}
// 完成解析某个结点
case XmlPullParser.END_TAG: {
if ("app".equals(nodeName)) {
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
break;
}
default:
break;
}

eventType = xmlPullParser.next();
} // end of while
} // end of if

SAX

Default Handler
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;

@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// 记录当前结点名
nodeName = localName;
}
@Override
public void characters(
char[] ch,
int start,
int length) throws SAXException {
// 根据当前的结点名判断将内容添加到哪一个StringBuilder对象中
if ("id".equals(nodeName)) {
id.append(ch, start, length);
} else if ("name".equals(nodeName)) {
name.append(ch, start, length);
} else if ("version".equals(nodeName)) {
version.append(ch, start, length);
}
}
@Override
public void endElement(
String uri,
String localName,
String qName) throws SAXException {
if ("app".equals(localName)) {
Log.d("ContentHandler", "id is " + id.toString().trim());
Log.d("ContentHandler", "name is " + name.toString().trim());
Log.d("ContentHandler", "version is " + version.toString().trim());
// 最后要将StringBuilder清空掉
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
@Override
public void endDocument() throws SAXException {
}
}
HttpClient httpClient = new DefaultHttpClient();
// 指定访问的服务器地址是电脑本机
HttpGet httpGet = new HttpGet("http://10.0.2.2:8080/get_data.xml");
HttpResponse httpResponse = httpClient.execute(httpGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");

SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();

// 将ContentHandler的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
// 开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));

JSON

JSON Object

HttpClient httpClient = new DefaultHttpClient();
// 指定访问的服务器地址是电脑本机
HttpGet httpGet = new HttpGet("http://10.0.2.2/get_data.json");
HttpResponse httpResponse = httpClient.execute(httpGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");

JSONArray jsonArray = new JSONArray(response);

for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
}
}

GSON

HttpClient httpClient = new DefaultHttpClient();
// 指定访问的服务器地址是电脑本机
HttpGet httpGet = new HttpGet("http://10.0.2.2/get_data.json");
HttpResponse httpResponse = httpClient.execute(httpGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");

Gson gson = new Gson();
List<App> appList = gson.fromJson(response, new TypeToken<List<App>>() {}.getType());

for (App app : appList) {
Log.d("MainActivity", "id is " + app.getId());
Log.d("MainActivity", "name is " + app.getName());
Log.d("MainActivity", "version is " + app.getVersion());
}
}

Network Best Practice

public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
public class HttpUtil {
public static void sendHttpRequest(final String address, final
HttpCallbackListener listener) {
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) {
// 回调 onFinish() 方法
// 将 response 传入回调方法
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
// 回调 onError() 方法
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
} // end of run
}).start(); // end of runnable
} // end of sendHttpRequest
} // end of class
//以后每当需要发起一条 HTTP 请求的时候就可以这样写:
String address = "http://www.github.com";
String response = HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 在这里根据返回内容执行具体的逻辑
}
@Override
public void onError(Exception e) {
// 在这里对异常情况进行处理
}
});

Map

Location

public class MainActivity extends Activity {

public static final int SHOW_LOCATION = 0;

private TextView positionTextView;

private LocationManager locationManager;

private String provider;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
positionTextView = (TextView) findViewById(R.id.position_text_view);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 获取所有可用的位置提供器
List<String> providerList = locationManager.getProviders(true);
if (providerList.contains(LocationManager.GPS_PROVIDER)) {
provider = LocationManager.GPS_PROVIDER;
} else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
provider = LocationManager.NETWORK_PROVIDER;
} else {
// 当没有可用的位置提供器时,弹出Toast提示用户
Toast.makeText(this, "No location provider to use",
Toast.LENGTH_SHORT).show();
return;
}
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
// 显示当前设备的位置信息
showLocation(location);
}
locationManager.requestLocationUpdates(provider, 5000, 1,
locationListener);
}

protected void onDestroy() {
super.onDestroy();
if (locationManager != null) {
// 关闭程序时将监听器移除
locationManager.removeUpdates(locationListener);
}
}

LocationListener locationListener = new LocationListener() {

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onLocationChanged(Location location) {
// 更新当前设备的位置信息
showLocation(location);
}
};

private void showLocation(final Location location) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 组装反向地理编码的接口地址
StringBuilder url = new StringBuilder();
url.append("http://maps.googleapis.com/maps/api/geocode/json?latlng=");
url.append(location.getLatitude()).append(",")
.append(location.getLongitude());
url.append("&sensor=false");
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url.toString());
// 在请求消息头中指定语言,保证服务器会返回中文数据
httpGet.addHeader("Accept-Language", "zh-CN");
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");
JSONObject jsonObject = new JSONObject(response);
// 获取results节点下的位置信息
JSONArray resultArray = jsonObject.getJSONArray("results");
if (resultArray.length() > 0) {
JSONObject subObject = resultArray.getJSONObject(0);
// 取出格式化后的位置信息
String address = subObject.getString("formatted_address");
Message message = new Message();
message.what = SHOW_LOCATION;
message.obj = address;
handler.sendMessage(message);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}

private Handler handler = new Handler() {

public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_LOCATION:
String currentPosition = (String) msg.obj;
positionTextView.setText(currentPosition);
break;
default:
break;
}
}

};

}


Sensor

SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
SensorEventListener listener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
}
};
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.unregisterListener(listener);

Light Sensor

Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

Accelerometer Sensor

Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Orientation Sensor

Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);


Best Practice

Global Context

<application android:name="com.example.networkTest.MyApplication">
......
</application>
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}

用 Intent 传递对象

Serializable

public class Person implements Serializable

Parcelable

public class Person implements Parcelable {
private String name;
private int age;

@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name); // 写出name
dest.writeInt(age); // 写出age
}

public static final Parcelable.Creator<Person> CREATOR = new
Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
Person person = new Person();
person.name = source.readString(); // 读取name
person.age = source.readInt(); // 读取age
return person;
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}

Custom Logger

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;
//custom key
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);
}
}
}