2014年9月1日 星期一

[Andorid] Service 範例-背景執行/無UI

要做一個Service要先了解,service無法自己啟動,必須要靠startService()才能啟動。

同樣的啟動後也必須要使用stopService()關閉。

在這邊需要注意的有幾點

1) startService()

2) stopService()

3) AndroidManiFest.xml增加service權限


第一點為開啟service必要條件,當然你要new一個intent

所以就是:

Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);

MainActivity為你目前的class,service_class則是另外開的java檔(那邊則是寫service做的事情)


第二點跟第一點很類似,只有差在start / stop的差別

Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);

第三點就是你需要在Android ManiFest.xml裡面新增權限

否則跑起來會有

"

Unable to start service Intent: not found

"
這樣的錯誤訊息出現

好了,總結上面 MainActivity該如何寫?

package com.example.service_nonui_demo;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this,service_class.class);
startService(intent);
}
@Override
public void onPause(){
super.onPause();
Intent intent = new Intent(MainActivity.this,service_class.class);
stopService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {

public PlaceholderFragment() {
}

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

}

Service部分

package com.example.service_nonui_demo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class service_class extends Service{

@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
 

@Override
public void onStart(Intent intent, int startId) {
Toast.makeText(this, "Service start", Toast.LENGTH_SHORT).show();
}
public void onDestroy(){
super.onDestroy();
Toast.makeText(this, "Service stop", Toast.LENGTH_SHORT).show();
}
}

(記得要extends Service!)

Android ManiFest.xml部分

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.service_nonui_demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.service_nonui_demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
            <service android:name="service_class"></service>
    </application>

</manifest>

記得要新增紅色字的這一行

如此一來,service就可以啟動了!

service在onStart()的地方開始運作,所以如果要做任何動作,可以寫在裡面喔!

範例只是做一個toast(跳出訊息)而已。

附上範例檔案 **點這裡下載**

End

2014年8月19日 星期二

[Android] 開機自動啟動APP/ 疑難雜症解答

在Android 系統裡面,要讓你的app在開機時候就啟動必須有兩個要件

1)BroadCastReceiver--這個東西是用來接收,android手機開機時會丟出的訊息(android.intent.action.BOOT_COMPLETED)

2)開權限--沒有路,怎麼走過去呢?
要在AndroidManiFest.xml增加權限,才能夠接收BOOT_COMPLETED

首先,先建立一支java class(在同一個類別下喔!)
像這樣

package com.example.thread_example;

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

public class BootAct extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent boot = new Intent(context,MainActivity.class);
                boot.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(boot);
}

}

這邊需要extends BroadcastReceiver 然後我們要改寫onReceive的內容,記得new Intent裡面是你要開啟的class名稱喔(這邊容易搞錯),然後使用startActivity來啟動。

權限的部分則是



綠色部分為所需要添加的程式碼,紅色部分則是要特別注意---名稱不要搞錯,像我是設為BootAct,搞錯就會出現Permission Denial: starting Intent .....這樣的錯誤。


OK,重新開機吧! 相信他重開之後就會開啟你的APP了。

2014年8月12日 星期二

[Android] UDP Receive / Send 完整-可執行-附檔案-教學

花了兩三個禮拜終於把Android UDP搞定!

首先,要先搞懂Thread / Handler是甚麼,不然這篇往下看沒有意義(因為會看不懂)

在MainActivity的部分,這邊負責主要的UI介面+Handler+Thread呼叫

package com.example.thread_example;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import android.annotation.SuppressLint;
import android.net.wifi.WifiInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.support.v7.app.ActionBarActivity;
import android.text.format.Time;
import android.util.Log;
import android.widget.TextView;

@SuppressLint("NewApi")
public class MainActivity extends ActionBarActivity {

public static Handler exHandler;
private TextView txv;
private ChatServer chatserver;
private ChatSender chatsender;
public static InetAddress IP;
public WifiInfo mwifiInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (android.os.Build.VERSION.SDK_INT > 9) {
   StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
   StrictMode.setThreadPolicy(policy);
}

txv = (TextView) findViewById(R.id.textView);
//利用handler來顯示接收到的文字
exHandler=new Handler() {
       @Override
       public void handleMessage(Message msg) {
        super.handleMessage(msg);
        String msgString = (String)msg.obj;
        Log.d("Handler","Now in Handler");
        txv.setText(null);
        txv.setText(msgString+"\n"+txv.getText().toString());
       }
   };
        //獲得Wifi的內部網路IP
   StringBuilder IFCONFIG=new StringBuilder();
   try {
       for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
           NetworkInterface intf = en.nextElement();
           for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
               InetAddress inetAddress = enumIpAddr.nextElement();
               if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
               IFCONFIG.append(inetAddress.getHostAddress().toString()+"\n");              
               }
           }
       }
       txv.setText(IFCONFIG.toString());
       Log.d("LOG_Text", IFCONFIG.toString());
   } catch (SocketException ex) {
       Log.e("LOG_TAG", ex.toString());
   }


try{
chatserver = new ChatServer();
chatserver.start();
chatsender = new ChatSender();
chatsender.start();
Log.d("User","Thread start...");

}catch(Exception e)
{
String str = e.toString();
Log.e("Error by User", str);
}

}

protected void onPause(){
super.onPause();
if(chatserver != null)
{
if(!chatserver.isInterrupted())
{
chatserver.interrupt();
}
}
}

}

有一點長,這邊元件只有拉一個TextView而已,十分簡單。

IFCONFIG 

這邊負責顯示WIFI的IP

Thread--chatserver/chatsender

則是另外開兩個java檔(與mainActivity.java同層)分別做為接收與傳送,.start()就是讓他啟動而已。

exHandler

這邊負責顯示接收到的資訊(不能用static來傳值,因為Android不支援,至於為什麼我也不清楚),並把他印出來在textview上面。



Receiver部分(chatserver.java)

package com.example.thread_example;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

import android.os.Bundle;
import android.os.Message;
import android.util.Log;

public class ChatServer extends Thread{
private DatagramSocket server = null;
private static final int PORT = 8000;
public ChatServer() throws IOException {
server = new DatagramSocket(PORT);
Log.d("User","new server socket");
}
public void run(){

byte[] byte1024 = new byte[1024];
//Message msg = new Message();
//Bundle data = new Bundle();
DatagramPacket dPacket = new DatagramPacket(byte1024, 100);
String txt;
try{
Log.d("User","runing run()");
while(true){
server.receive(dPacket);
while(true)
{
//印出來到螢幕上
txt = new String(byte1024, 0, dPacket.getLength());
MainActivity.exHandler.sendMessage(MainActivity.exHandler.obtainMessage(1,txt));
Log.d("User","Handler send Message");
if(true) break;
}
//CloseSocket(client);//關閉
}
}
catch(IOException e)
{}
}
private void CloseSocket(DatagramSocket socket) throws IOException{
socket.close();
}
}

這邊宣告一個1024的byte來接收資料(按照你的資料量大小來調整宣告大小)

一定要宣告datagramSocket,datagramPacket,一個是連線,一個是接收封包用,至於handler這邊的

sendMessage就是回傳到MainActivity來讓textview顯示接收的資訊而已。結構上有兩個while(),第

一個while會卡在receive()這邊,一直到有封包進來才會往下跑到第二個while(),第二個則是印出資

訊,沒有甚麼特別的地方(Log是debug用的)



[Android] java.net.BindException(address already in use) 錯誤訊息 解決方法 / UDP

在寫UDP的時候遇到了一個問題:

java.net.BindException(address already in use)

這個意思是說你在短時間內訪問過多次這個port

解決方法有2種

1) 修改Port,理由是:有可能你的電腦其他程式也在使用此port,因此造成Error
     (第一次修改後有成功,後來不成功所以又找到第二種方法)

2) 減緩你訪問Port的次數,比方說Thread.Sleep(1000)就改成2000,3000,4000,....等,請自行測試
     (修改成10000時,就沒有出現此問題了)

Android UDP的傳送端較容易產生此問題,End.

2014年8月11日 星期一

[Android] 解決NetworkOnMainThreadException問題

雖然網路上文章滿多的,但還是紀錄一下以免忘記。

會有這個問題產生主要是在Android 4.0版本以上才會出現,確切原因不清楚,但應該是增加安全

性理由所以才修改成這樣的(?)

這問題就是如同字面上意思:

你在主線程上面呼叫Network類相關API

這時候該如何解決呢??

1)使用分流法(開個新的Thread)來操作網路,但如果我就是要調整並顯示UI介面呢?

2)在setContentView(R.layout.activity_main)下面新增
    if (android.os.Build.VERSION.SDK_INT > 9) {
   StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
   StrictMode.setThreadPolicy(policy);
       }

即可解決該問題