●補充說明--加密

(一)HASH(雜湊):不能解密,不能反算,單向加密,用在數位簽章和密碼雜湊
1.MD5
2.SHA1
 
(二)明文,雙向的,可以解密
1.RSA:非對稱式加密法(速度慢)
=>用A(公)金鑰加密,只能用B(私)金鑰解密,用B金鑰加密,只能用A金鑰解密
=>先用公鑰加密,把公鑰和資料傳過去,私鑰留著,收到者再用私鑰解密
=>SSL加密:先建立RSA加密連線,公鑰傳過去,對方把DES加密金鑰丟回,再用私鑰解開得到對方的DES加密金鑰
=>因為SSL加密駭客會用騙公鑰的做法,假造公鑰攔截丟過去在繼續後面傳輸動作,所以後來都把公鑰放在第三方公正機構(要收費),所以有些網站像是學校或是一些政府單會,會跳出簽章不可信任的訊息,因為沒有經費
 
2.DES對稱式加密法(速度快),建議專案使用
=>A金鑰加密,A金鑰解密

 

●程式說明--AES加密(代替DES,做完加密以後,還要把程式混亂,可以使用proguard反編譯工具)

1.先在build.gradle中加入compile 'com.scottyab:aescrypt:0.0.1'
2.程式如下:
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          
        String password = "password";
        String message = "TESTTESTTEST";
        try {
            //加密  
            String encryptedMsg = AESCrypt.encrypt(password, message);
            Log.d("ENC", encryptedMsg);
        }catch (GeneralSecurityException e){
            //handle error
        }
        // dT4GAbl/CWOzjueYjSB5WA==
        String encryptedMsg = "dT4GAbl/CWOzjueYjSB5WA==";
        try {
            //解密
            String messageAfterDecrypt = AESCrypt.decrypt(password, encryptedMsg);
            Log.d("ENC", messageAfterDecrypt);
        }catch (GeneralSecurityException e){
            //handle error - could be due to incorrect password or tampered encryptedMsg
        }
 
    }
}
=>加密後可以用LOG看到密碼為第一行,解密就可以得到原始輸入密碼
android aes加密
=>可以把加密後的密碼存在文字檔,要用再讀出來解密

 

●程式參考(GitHub):Android AES加密(使用com.scottyab:aescrypt)

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●程式說明--多重執行緒補充

=>run倒數計時

(一)conuntDownTimer=>非多重執行緒,是在主執行緒做
public class MainActivity extends AppCompatActivity {
    TextView mTextField;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextField=(TextView)findViewById(R.id.mTextField);
        //每秒鐘執行一次,總共執行30CountDownTimer,都是在主執行緒執行
        new CountDownTimer(300001000) {
           //倒數時要run的程式
            public void onTick(long millisUntilFinished) {
                mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
            }
            //結束後要run的程式
            public void onFinish() {
                mTextField.setText("done!");
            }
        }.start();
    }
}
**說明:
1.CountDownTimer (long millisInFuture, long countDownInterval)
=>Parameters
millisInFuture long: The number of millis in the future from the call to start() until the countdown is done and onFinish() is called.
countDownInterval long: The interval along the way to receive onTick(long) callbacks.
 
=>問題:因為onTick和onFinsish都是在主執行緒上跑,如果在這兩個method中跑很花時間的程式,還是會讓程式卡住ex,如果再onTick中加入,就會跑一下跳過兩秒,29=>26=>23....程式被卡住了
try {
    Thread.sleep(2000);
catch (InterruptedException e) {
    e.printStackTrace();
}
 
(二)使用runOnUi
public void runonui(View view) {
        new Thread(){
            int 5;
            @Override
            public void run() {
                super.run();
                do {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv2.setText(String.valueOf(i));
                        }
                    });
                    i--;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } while (i>=0);
            }
        }.start();
    }
}
 
(三)使用Handler
public class MainActivity extends AppCompatActivity {
    TextView tv3;
    Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv3=(TextView)findViewById(R.id.tv3);
handler=new Handler();
}
public void onhandler(View view) {
new Thread(){
int i = 5;
@Override
public void run() {
     super.run();
do {
     handler.post(new Runnable() {
     @Override
     public void run() {
          tv3.setText(String.valueOf(i));
     }
     });
i--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (i>0);
}
}.start();
}
}
**說明:
1.handler是android才有,java沒有的class
2.handler的method會在宣告handler的執行緒上run
3.這裡在主執行緒上宣告handler物件,在其他執行緒上用handler.post(~),就會在主執行緒上執行post中的內容
4.實際上run的方法則是和runOnUi一樣,但使用上比較彈性,不限定只能在主執行緒上跑
 
(四)AsynTask=>處理執行緒和ui的機制,有六大function
=>因為是抽象類別而且有抽象方法,所以要繼承和override
=>繼承的時候,可以看到有三個泛型參數
1.執行 AsynTask要傳甚麼類型的資料(傳入的型別)
2.回報進度的型別
3.結果的型別(ex:如果是抓網路文字資料就是字串,如果是抓網路圖片就是bitmap)
 
//建立一個內部類別繼承AsynTask
class MyAsynTask extends AsyncTask<Integer,Integer,String>{
    //因為要使用MainActivity的tv4但是不要設定static,就利用建構式把tv4傳進來
    TextView tv;
    public MyAsynTask(TextView tv){
        this.tv=tv;
    }
 
 
    //override doInBackground方法=>在背景執行
    //這邊的參數類型會和繼承時決定的傳入型別一樣,...是參數列表與陣列類似,可以一個或多個參數
    //ex:abc(int [ ] x):void; 或 abc(int... x):void;
    @Override
    protected String doInBackground(Integer... integers) {
        int n = integers[0];
        int i;
        for(i=n;i>=0;i--)
        {
            Log.d("TASK", "i:" + i);
            //publishProgress(Progress... values): to publish updates on the UI thread while the background computation is still running
            //參數:values Progress: The progress values to update the UI with.
            publishProgress(i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Returns true if this task was cancelled before it completed normally
            if (this.isCancelled() == true)
            {
                break;
            }
        }
        return "OK";
    }
 
 
    @Override
    //在執行之後
    //會在doInBackground執行完之後才執行,而且是在主執行緒執行,並且doInBackground return的內容會丟到這邊的參數帶入
    //所以看log這邊會顯示ok
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        Log.d("TASK", s);
    }
 
 
    @Override
    //進度更新,也是在主執行緒跑
    //這邊的Integer...values是MyAsynTask<>第二個帶入的參數類型,由publishProgerss()傳值過來
    //必須在 doInBackground加上publishProgress();
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        tv.setText(String.valueOf(values[0]));
    }
 
 
    @Override //當呼叫cancel method,會run onCancelled()
    /*onCancelled
    Applications should preferably override onCancelled(Object). This method is invoked by the default implementation of onCancelled(Object).
    Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.*/
    protected void onCancelled() {
        super.onCancelled();
 
 
    }
}

 

 

●程式參考(GitHub):多重執行續的補充說明(CountDownTimer、Handler、runOnUiThread、AsynTask)

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●程式說明

(一)Firebase Realtime Database

=>使用雲端資料庫交換資料

=>先連結google帳號和firebase

 
1.開新專案=>tools=>firebase
firebase_realtime_database
 
2.右邊開啟視窗,選Realtime Database
firebase_realtime_database
 
3.點Save and retrieve data,第一步先connect to Firebase,完成後會變成綠色的
firebase_realtime_database
 
用project模式看,會發現app下面自動產生了一個google-service.json檔,裡面存了金鑰
firebase_realtime_database
 
4.第二步點add the realtime database to your app,會自動幫你做好設定
firebase_realtime_database
 
選擇是否要在firebase新建專案 或是使用舊專案
firebase_realtime_database
 
5.第三步,回firebase看到新建的專案,點進去=>database=>開始使用=>規則,會看到如下內容
=>"auth != null"代表驗證是空的就不行讀取,沒驗證的話不能讀取
=>把內容改成read:true,write:true
=>發佈
firebase_realtime_database
 
=>規則公開,任何人都可以讀取,開放資料庫讓app讀取
firebase_realtime_database
 
(二)回到android程式
public class MainActivity extends AppCompatActivity {
TextView tv;
EditText et;
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef;
 
@Override.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=(TextView)findViewById(R.id.tv);
et=(EditText) findViewById(R.id.et);
}
 
public void write(View view) {
myRef = database.getReference("message");
myRef.setValue(et.getText().toString());
}
 
public void read(View view) {myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
String value = dataSnapshot.getValue(String.class);
tv.setText(value);
 
}
 
@Override
public void onCancelled(DatabaseError error) {
// Failed to read value
//Log.w(TAG, "Failed to read value.", error.toException());
}
});
}
}
**說明:
1.FirebaseDatabase database = FirebaseDatabase.getInstance();=>連結資料庫
2. myRef = database.getReference("message");=>連結資料,資料名稱為message
3.myRef.setValue=>把message資料寫入
4.寫入完成可以去firebase看資料,可以看到message中有value
5.讀取時,增加一個值的監聽器myRef.addValueEventListener ,隨時監聽資料有無改變,當值一改變就觸發下面的程式,因為是放在click裡面,所以只要點一次按鈕,就會一直持續監聽,如果要取消監聽,可以用removelistener,但是remove的話要把監聽做成一個物件,才能使用
 
=>資料可以有很多reference,大量資料可以存成jason來用
 
 
(三)全域變數如果要同時給很多個activity使用,有兩種做法
1.直接在mainactivity宣告:public static String 變數
2.宣告一個類別來繼承application,在這個類別中宣告public 變數,不需要static
firebase_realtime_database
public class MyApplication extends Application {
    public String str;
}
 
並且在AndroidManifest.xml中設定application去抓myapplication,這邊的設定是告訴android要呼叫哪一個class來當最底層的application,所以我們要把最底層的系統application改成我們的application
<application
    android:name=".MyApplication"
    ...
</application>
 
在要使用變數的activity中加入下列程式,因為在啟動程式時,會先啟動一個最底層的application物件,然後mainactivity會放在上面,而getApplication是AppCompatActivity中的方法,可以取得這個activity最底層的application物件並回傳,但因為我們要使用我們自己寫的MyApplication,所以要轉型為我們的類別,這樣就可以直接透過.str使用str變數
MyApplication app = (MyApplication) getApplication();
app.str = ...(省略)

 

 

 

●程式參考(GitHub):透過Android Studio內建機制使用FireBase Realtime Database

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

6/21 

My son finally finished his final exam yesterday and his grade of Math is 90 marks.

No matter what the result, both me and my son could take a load off our mind.

 After dinner, we drew pictures together and had fun.

 

6/22

My birthday falls on a weekday, so we plan to celebrate my birthday at this weekend. 

We are going to a famous Korean restaurant and the Banchan there is really yummy.

After lunch, we plan to buy the trolley bag for our tirp on July.

 

6/23

We didn't find the trolley bag  which matched up my imagination yesterday.

My period was coming and I had cramps all last night.

In spite of sleeping for a long time last night, I still felt tired this morning.

 

6/24

I had a fight with my husband again.

To draw attention away from negative emotions, I started to play a mobile game.

The game was so much fun that I didn't sleep util 1:30 am and I was really tired.

 

6/25

My co-worker who is also a developer takes leave today because he has a job interview.

After yesterday's meeting, he made up his mind to give notice before July.

I have to say I really envy him for getting opportunities for interviews soon.

 

6/26

My boss arranges for a meeting at 5:00 pm today.

Both my co-worker and I think my boss's arrangement  is to make us work more than we are paid.

Good news is that there will be four newcomers to join us at July 1.

 

6/27

My son's summer vacation is coming and I counld finally get up a bit late in the morning.

Howerver, I have to plan for his summer vacation.

I have to plan weekly summer homework schedule for my son and arrange for him to participate in fun and educational activities.

 

6/28

One of our client held a seminar today and used the face recognition of our company to welcome their VIP guests.

However, the client wasn't satisfited with our system.

My boss was very unhappy and called a post-mortem meeting right away.

 

6/29

All my family went to the Taipei Astronomical Museum today.

The museum recently had organized a special exhibition which displays the history, astronaut equipment, food and so on for moon landing.

After seeing the exhibition, we went to see a movie about Mars landing and the movie was really great.

 

6/30

My son and I plan to visit his best friend today after his Taekwondo class.

I take my son for a motorcycle ride but it's really dangerous.

Although it's dangerous, it's more convenient than taking public transportation.

 

 


 

*finish the final exam:結束期末考

*take a load off one's mind:放下心中重擔

*....falls on a weekday:...日子是工作日

*Banchan:韓式餐廳的小菜(拌菜)

*trolley bag:有拖輪的旅行袋

*match up my imagination:符合我的想像

*in spite of Ving:雖然....

*draw attention away from...:將注意力從...轉移

*have a job interview:有面試

*yeterday's meeting:昨天的會議

*make up one's mind:下定決心

*give notice:提辭呈

*envy sb. (for) sth.:羨慕某人某事

*get an opportunity for an interview:取得面試的機會

*arrange for ...:安排

*newcomer:新人

*summer homework:暑假作業

*Taipei Astronomical Museum:台北天文館

*organize a special exhibition:舉辦一個特展

*moon landing:登月

*seminar:研討會

*welcom VIP guests:迎賓

*call a meeting:召開會議

*post-mortem:屍檢,事後檢討

*take a child for a motorcycle ride:騎機車帶小孩 

*public transportation:大眾交通工具

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●程式說明

(一)使用ImageRequest=>需要七個參數:url、成功要做甚麼、最大寬、最大高、 scaletype、顏色、失敗做甚麼
 
RequestQueue queue= Volley.newRequestQueue(MainActivity.this);
ImageRequest request1=new ImageRequest("http://images.parents.mdpcdn.com/sites/parents.com/files/styles/width_300/public/images/p_101395269.jpg",
        new Response.Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                ImageView iv1=(ImageView)findViewById(R.id.iv1);
                iv1.setImageBitmap(response);
            }
        }, 0, 0, ImageView.ScaleType.FIT_XY, Bitmap.Config.RGB_565
        , new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    }
});
queue.add(request1);
 
**說明:
1.要先拉ImageView
2.最大寬高設為0的時候,代表不設定寬高限制
3.顏色通常都用RGB_565

 

(二)使用picasso
=>好處是會作暫存,如果都從同一個網站下載圖片,以新聞網為例,可能幾分鐘後又去看一次新聞,picasso會自動偵測暫存,如果有資料就不會再次下載,但使用ImageRequest則會重複下載,看一次就會下載一次
=>加build.gradle compile 'com.squareup.picasso:picasso:2.5.2'
 
ImageView iv2=(ImageView)findViewById(R.id.iv2);
Picasso.with(this)
        .load("https://thumb1.shutterstock.com/display_pic_with_logo/3138983/499778260/stock-vector-cartoon-ladybug-vector-illustration-cute-red-ladybug-isolated-in-a-flat-style-499778260.jpg")
        .into(iv2);

 

**上述兩種做法的缺點:當要載入的圖片很大時,不會出現進度條,要等全部載完才會有反應,但可以使用轉圈圈的方式

 

●程式參考(GitHub):使用volley ImageRequest和Picasso下載圖片資源

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●補充說明​​

(一)用volley來寫網路的存取程式

=>volley是google官方作的,且為google官方推薦的第三方元件,已經把抓資料的細節都包好了

=>使用volley有四個步驟

1.建立queue(佇列)=>排隊

2.建立request(要求)=>去存取一個網路資源的要求,執行第二個步驟時,要丟三個參數

=>

a.url

b.成功了要做甚麼=>這是在主執行緒上做,所以可以動到ui

c.失敗了要做甚麼

3.把2加入1(讓request排隊)

4. 1.start();(叫隊伍開始動)

=>做之前要先設定權限

<uses-permission android:name="android.permission.INTERNET"/>
 
=>在build.gradle(android建置系統的設定檔)的dependencies中加入下面內容
dependencies {
   ....
    compile 'com.android.volley:volley:1.0.0'     
}
**說明:
這個做法就可以直接外掛這個library進來,完成後點上面的sync now,此時網路不能斷,因為會去抓library
 
(二)openData的概念:政府利用人民的稅金收集到很多資料,應該要去識別化(和隱私脫鉤),讓人民能夠很方便的使用這些資料
 
(三)使用openData做的app可以賣錢也可以掛廣告,行政院的態度是不能賣raw data,但是可以加值資料(ex變成圖片),就可以賣錢,但部分下屬單位如高公局的即時車速就規定不能賣錢
 
(四)JSON:javascript轉出來的物件標註格式
=>這邊以新北市政府動物之家的資料為例,做json之前要先看資料格式
=>[]代表陣列,{}代表物件
=>可以看到這個資料是一整個陣列,裡面包了很多物件
=>district、address這些是屬性,八里、八里區長坑里...這些是內容
Snap4.png

 

●程式說明

(ㄧ)Volley的部分

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RequestQueue queue= Volley.newRequestQueue(MainActivity.this);
        StringRequest request=new StringRequest("http://google.com.tw", new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d("Net",response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("NET",error.toString());
            }
        });
        queue.add(request);
        //queue.start();
    }
}
 
**說明:
1.因為queue只能有一個,所以不能new一個RequestQueu,而是透過 Volley.newRequestQueue來取得queue
=>這是一個Singleton/single pattern的作法:當有一些系統上的資源,你希望那個物件在系統上只有一份,唯一的一份不要有第二份,就不能讓它new,要做一個static method,利用這個靜態method傳回你要傳回的物件
2. StringRequest我們這邊用三個參數的版本(還有四個參數的版本),上網抓到的字串就會放在 String response中
3.如果要抓圖片可以用ImageRequest
4.要注意的是,最後不需要使用queue.start(),因為Volley在初始化RequestQueu時已經call過start()了,如果在呼叫一次會出現錯誤訊息:com.android.volley.NoConnectionError: java.io.InterruptedIOException

 

(二)Json的部分可以分為兩種做法

作法一:使用JSONArray和JSONObject

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RequestQueue queue= Volley.newRequestQueue(MainActivity.this);
        StringRequest request=new StringRequest("http://data.ntpc.gov.tw/od/data/api/BF90FA7E-C358-4CDA-B579-B6C84ADC96A1?$format=json", new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONArray array=new JSONArray(response);
                    for (int i=0;i<array.length();i++){
                        JSONObject obj=array.getJSONObject(i);
                        Log.d("Net","district:"+obj.getString("district"));
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("NET",error.toString());
            }
        });
        queue.add(request);
    }
}
 
**說明
1.把抓下來的jSON資料放在response中,就可以轉出來變成json陣列
2.用迴圈去跑每一個array的物件取出來,然後用log顯示該署性的內容(因為要用getString,所以不能用foreach)
3.因為這份資料是用array包裹物件,所以是用JSONArray 去轉,如果是用物件包裹array,就要用 JSONObject去轉(依資料格式來決定)

 

作法二:利用gson函式庫(https://github.com/google/gson) 也是google的

=>一樣要在build.gradle中掛library
compile 'com.google.code.gson:gson:2.8.2'
=>看raw data的資料有四個屬性,要先寫一個類別加上四個屬性,內容必須和資料中完全一樣
 
自建類別:AnimalHome.java
public class AnimalHome {
public String district;
public String address;
public String tel;
public String opening_hours;
}

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RequestQueue queue= Volley.newRequestQueue(MainActivity.this);
        StringRequest request=new StringRequest("http://data.ntpc.gov.tw/od/data/api/BF90FA7E-C358-4CDA-B579-B6C84ADC96A1?$format=json", new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Gson gson=new Gson();
                AnimalHome[] ah;
                ah=gson.fromJson(response,AnimalHome[].class);
                for(AnimalHome a:ah){
                    Log.d("net",a.district);
                    Log.d("net",a.address);
                }
                String str = gson.toJson(ah);
                Log.d("NET", str);
                ArrayList<AnimalHome> list1 = new ArrayList<>();
                for (AnimalHome a : ah)
                {
                    list1.add(a);
                }
                String str2 = gson.toJson(list1);
                Log.d("NET", str2);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("NET",error.toString());
            }
        });
        queue.add(request);
    }
}
**說明:
1.起一個gson物件
2.因為要用泛型,所以先建立我們自訂類別AnimalHome的陣列
3.用fromJson方法看下圖,第一個參數是json的字串內容,第二個參數是泛型類別(T就是Type),也就是要轉出來的型別,這邊我們要轉成我們自訂的類別,寫法為
AnimalHome[].class,把json的每一個屬性放進class中(即把json字串內容--resopnse轉成自訂的類別的陣列)
volley
4.透過foreach迴圈,抓陣列每個屬性的值
5.如果要把陣列或arraylist或是string再轉回jason,就用 gson.toJson
6.測試,建立一個新的arraylist <AnimalHome>,把ah中的每一筆資料透過foreach迴圈放到新建的arraylist中,再用 gson.toJson轉成JSON
=>當資料大概幾百筆,且不需要很多搜尋和篩選,就可以用此做法把ArrayList存成JSON格式(文字檔),隨時可以讀出來再轉成Arrayloist(非常簡單的存檔作法,修改刪除只需要用add和remove就可以,不需要動用到資料庫)
=>當資料量大且需要很多搜尋和篩選時,才需要使用資料庫

 

●用volley抓網路資料可能會遇到的問題(以抓聯合新聞的rss為例)
1.先授權<uses-permission android:name="android.permission.INTERNET"/>
2.build.gradle加入compile 'com.android.volley:volley:1.0.0'
3. 如果直接用之前的做法會發現抓回來的資料都是亂碼
RequestQueue queue= Volley.newRequestQueue(MainActivity.this);
StringRequest request=new StringRequest("https://udn.com/rssfeed/news/2/6638?ch=news",
        new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d("NET", response);
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
 
    }
});
queue.add(request);
volley
 
=>這是因為聯合新聞網沒有在xml的開頭準確的標示編碼要用utf8,所以volley不知道要用utf8編碼去處理
=>有些網站有這個問題還不會出現亂碼,而是直接沒有反應,這樣就會很難查
 
=>做法:自己寫一個utf8StringRequest class繼承StringRequest
 
public class UTF8StringRequest extends StringRequest {
public UTF8StringRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
public UTF8StringRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(url, listener, errorListener);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
 
String utf8String = null;
try {
utf8String = new String(response.data, "UTF-8");
return Response.success(utf8String, HttpHeaderParser.parseCacheHeaders(response));
 
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
}
**說明:
1.utf8String = new String(response.data, "UTF-8");
這邊的response.data是一個byte array,要用UTF-8的方法轉成字串
2.再把MainActivity.java的
StringRequest request=new StringRequest 改成 StringRequest request=new UTF8StringRequest
 
 

●程式參考(GitHub):

實作透過Volley、JSONArray、JSONObject、GSON取得Open Data資料

客製化UTF8StringRequest

 

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●程式說明

=>把抓到的rss新聞標題用listview的方法呈現出來

=>點擊每一個listview就要抓到它的link連出去(丟到下一頁,載入webview)

=>再寫一個arraylist不是很好的做法,比較好的做法是寫一個類別來存rss的資料,或是用MAP/SimpleAdaper來做
 
(一)先自訂一個類別存放rss資料:RSSNewsItem=>這邊不用get和set,只放兩個field
public class RSSNewsItem {
    public String title;
    public String link;
}

 

(二)MyDataHandler

public class MyDataHandler extends DefaultHandler {
    boolean isTitle = false;
    boolean isItem =false;
    boolean isLink = false;
    public ArrayList<RSSNewsItem> data = new ArrayList<>();
    RSSNewsItem item;
 
 
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("title"))
        {
            isTitle = true;
        }
        if (qName.equals("item"))
        {
            isItem = true;
            item = new RSSNewsItem();
        }
        if (qName.equals("link"))
        {
            isLink = true;
        }
 
 
    }
 
 
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if (qName.equals("title"))
        {
            isTitle = false;
        }
        if (qName.equals("item"))
        {
            isItem = false;
            data.add(item);
        }
        if (qName.equals("link"))
        {
            isLink = false;
        }
    }
 
 
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        if (isItem && isTitle)
        {
            item.title = new String(ch, start, length);
            //Log.d("NET", new String(ch, start, length));
        }
        if (isItem && isLink)
        {
            item.link = new String(ch, start, length);
            // Log.d("NET", new String(ch, start, length));
        }
    }
}
 
**說明:
1.public ArrayList<RSSNewsItem> data new ArrayList<>();=>使用泛型來儲存 RSSNewsItem 物件
RSSNewsItem item;=>設定item為 RSSNewsItem物件
2.但抓回來資料會發現第一個頁面標題也被抓進來了,我們只需要在item tag中的標題,所以多一個判斷變數isItem=>當tag開始為item為true,並且最後判斷要isItem和isTitle都是true才抓標題資料,boolean isLink false; =>用來判斷link tag的開頭結尾
2.在判斷item開頭結尾的時候,如果是開頭,就立刻new一個 RSSNewsItem物件,如果是結尾,就把這個物件加到data這個arraylist中
3.增加isLink的判斷
4.最後判斷如果是在item中而且是title中的內容,就把資料存進item.title中; 如果是在item中而且是link中的內容,就把資料存進item.link中
 

(三)MainActivity.java

public class MainActivity extends AppCompatActivity {
    ListView lv;
    ArrayAdapter<String> adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv=(ListView)findViewById(R.id.listview);
    }
 
 
    public void click(View view) {
        new Thread(){
            @Override
            public void run()
            {
                URL url = null;
                try {
                    url = new URL("https://udn.com/rssfeed/news/2/6638?ch=news");
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.connect();
                    InputStream inputStream;
                    inputStream = conn.getInputStream();
 
 
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                    final StringBuilder sb = new StringBuilder();
                    String str;
                    while ((str = br.readLine()) != null)
                    {
                        sb.append(str);
                    }
                    String result = sb.toString();
                    final MyDataHandler dataHandler = new MyDataHandler();
                    SAXParserFactory spf = SAXParserFactory.newInstance();
                    SAXParser sp = spf.newSAXParser();
                    XMLReader xr = sp.getXMLReader();
                    xr.setContentHandler(dataHandler);
                    xr.parse(new InputSource(new StringReader(result)));
 
 
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ArrayList<String> forAdaper=new ArrayList<String>();
                            for (RSSNewsItem i:dataHandler.data){
                                forAdaper.add(i.title);
                            }
                            adapter=new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,forAdaper);
                            lv.setAdapter(adapter);
                            lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                                @Override
                                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                                    Intent in=new Intent(MainActivity.this,Detail.class);
                                    in.putExtra("link",dataHandler.data.get(i).link);
                                    startActivity(in);
                                }
                            });
                        }
                    });
 
 
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (ProtocolException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ParserConfigurationException e) {
                    e.printStackTrace();
                } catch (SAXException e) {
                    e.printStackTrace();
                }
            }
 
 
        }.start();
    }
 
**說明:
1.前面都和抓rss作法一樣,但因為listview是在主執行緒,只有建立ui的執行緒才能使用它,要回ui thread,就要使用 runOnUiThread
2.透過arrayadapter把arraylist的資料丟給listview
3.因為listview只需要放title資料不需要放link資料,而且現在data這個arraylist中存放的是RSSNewsItem物件,所以這邊要再建一個 ArrayList<String> forAdapter,用來存放從data中抓出來的title資料
2.透過foreach迴圈,把data中的每一個title加到forAdapter中
3. new ArrayAdapter<String>時的資料來源就要改為forAdapter
4.設定OnItemClick監聽,並啟動intent
5.要丟過去的資料是 dataHandler.data.get(i).link=>data這個arraylist中的第i項物件,因為是 RSSNewsItem物件,所以可以用.link取得這個物件的link資料
 
 
(四)Detail.java
public class Detail extends AppCompatActivity {
WebView wv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
wv=(WebView)findViewById(R.id.wv);
wv.getSettings().setJavaScriptEnabled(true);
wv.setWebChromeClient(new WebChromeClient());
Intent in=getIntent();
String url=in.getStringExtra("link");
wv.loadUrl(url);
}
**說明:
1.設定webview=>javascript和 WebChromeClient
2.setWebChromeClient()void setWebChromeClient (WebChromeClient client)
=>Sets the chrome handler. This is an implementation of WebChromeClient for use in handling JavaScript dialogs, favicons, titles, and the progress. This will replace the current handler.
=>Parameters
client WebChromeClient: an implementation of WebChromeClient
3.用getIntet取得前面丟過來的intent,再用 getStringExtra取得前面丟過來的link資料,把link放到loadUrl中

 

●程式參考(GitHub):實作讀取新聞網站的RSS

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●畫面預覽

=>點擊SAX按鈕後,下方出現Udn RSS新聞列表

Screenshot_2019-06-14-10-42-37.png

 

●補充說明

(一)RSS的讀取(讀取新聞網站的RSS)
=>RSS是XML格式,android讀進來以後要再用XML的解析工具解開,然後再從中找出我們要的內容
=>RSS一般是要使用RSS閱讀器,指定RSS位置,就可以看到標題和簡短的內容(不能看全文是因為要導引到網站,商業考量)
=>資料交換的格式有:CSV(用,分開)、XML、JSON
=>在JAVA中解XML有兩種方法
 
1.XML DOM Parser=>xml有結構,作法是把整份文件讀下來再解成整顆樹,是一個樹狀資料結構,再用樹的walkthrough方法,從樹的根開始一條條走,用樹的軸覽方法把資料讀取出來
Rss Reader
 
 
2.SAX:讀到元件或文字就會跳出相對應的事件出來(我們自己寫的程式),一步一步做
=>一樣要先開起權限<uses-permission android:name="android.permission.INTERNET"/>
(MainActivity)
public void click(View view) {
new Thread(){
@Override
public void run()
{
URL url = null;
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.connect();
InputStream inputStream;
inputStream = conn.getInputStream();
 
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
final StringBuilder sb = new StringBuilder();
String str;
while ((str = br.readLine()) != null)
{
sb.append(str);
}
String result = sb.toString();
MyDataHandler dataHandler = new MyDataHandler();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setContentHandler(dataHandler);
xr.parse(new InputSource(new StringReader(result)));
 
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
}.start();
}
**說明:
1.sax這幾行的作法是固定的,可自己研究內容
2.setContentHandler(dataHandler),設定xml讀取器為我們的class
3. xr.parse(new InputSource(new StringReader(result)));這邊把rss資料載完
 
(MyDataHandler)=>要自己寫一個class,繼承DefaultHandler(繼承的時候要選org.xml這一個繼承),並override三個method
Rss Reader
public class MyDataHandler extends DefaultHandler {
boolean isTitle = false;
boolean isDes=false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (qName.equals("title"))
{
isTitle = true;
}
}
 
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if (qName.equals("title"))
{
isTitle = false;
}
}
 
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
if (isTitle)
{
Log.d("NET", new String(ch, start, length));
}
}
}
**說明:
擷取.PNG
=>每一個item的結構都是item中有title、link、pubDate、descritpion、guid
1.這邊的qName就是tag的名稱(title)
2.最後把整個字元陣列ch取出來轉成字串,同時要取start和length才不會有問題
3.characters():void characters (char[] ch, int start, int length)
=>Receive notification of character data inside an element.
=>By default, do nothing. Application writers may override this method to take specific actions for each chunk of character data
(such as adding the data to a node or buffer, or printing it to a file).
=>Parameters
ch-char: The characters.
start-int: The start position in the character array.
length-int: The number of characters to use from the character array.

 

 

(二)RSS xml的取得

=>以udn新聞網為例,選取上方RSS,再到下方選定要Feed的rss類別

Snap1.png

=>開啟RSS XML 的URL位址

Rss Reader

 

 

●程式參考(GitHub):實作讀取新聞網站的RSS

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

自訂txt檔案並匯入app中,做法如下:

(一)在電腦建立一個檔名為flashcard.txt的檔案

單字卡

(二)建立資料的規則為:

群組/資料夾名稱:寫在每一行的開頭,並且和單字內容以:區隔(注意這邊:不可以使用全形或中文的:)

單字卡:每個單字卡與中文都用,隔開(注意這邊,不可以使用全形或中文的,)

如果同時要建立的資料夾/群組不只一個,在每一個群組/資料夾的結尾用@區隔

範例如下:

單字卡

**存檔時要注意,一定要存成UTF-8的格式,不然中文就會出現如下圖的亂碼

單字卡

單字卡

(三)將檔案放進app中的方法有兩種

1.使用電腦連接手機放入檔案

a.手機接到電腦後,開啟內部共用儲存空間

單字卡

b.尋找一個名稱為FlashCard的資料夾

(有時候連接手機後會找不到這個資料夾,手機重開機即可找到)

單字卡

c.將剛剛完成的txt檔放入,如果裡面已經有一個flashcard.txt可以直接覆蓋

單字卡

2.透過雲端硬碟直接下載後放到手機中的資料夾

a.將txt檔放到雲端硬碟(ex:google drive/dropbox...)

b.下載到手機中(一般會下載到download資料夾中,每款手機可能會有差異)

c.將檔案從下載後的資料夾移動至內建儲存空間中的FlashCard資料夾即可

(一樣可直接覆蓋相同名稱的檔案)

單字卡

 

(四)完成後,點擊"批次匯入txt檔按鈕"

Screenshot_20190613-173055.jpg

 

(五)匯入成功回到資料夾/群組首頁,可以看到在txt檔內輸入的資料

單字卡

單字卡

單字卡

單字卡

 

單字卡app下載連結:google play

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

6/11

Our HR specialist was still angry and yelled at me this morning, so I told her that I didn't speak ill of her behind her back.

And after that, she could finally talk to me just as usual.

My workloads have increased recently and I'm worried that I can't handle it.

 

6/12

In the beginning I loved going to work because I really loved programming.

However, after all these days, I am in low spirits as soon as I think of going to work.

Because my co-workers would snitch on others.

 

6/13

My son had an English review quiz to review for the final exam yesterday .

I am worried about him because he always makes careless errors on exam.

However, he got really good grades and his grade was 99 marks.

 

6/14

Today is my son's English final Exam and I really hope he won't make careless errors again.

Last night I complained to my husband about my boss.

My husband didn't comfort me. Instead, he preached to me and that depressed me more.

 

6/15

I was bloated and have the hiccups all day long yesterday.

I was worried that I had a stomach ulcer again.

Having a gastroscopy is really uncomfortable.

 

6/16

Last night, I prepared the question paper for my son to review for his final exam next week.

I didn't sleep until it was 2:00 am and I felt really tired.

It's really strange that I prepare his final exam harder than he do.

 

6/17

I was finally not bloated and didn't keep having the hiccups this morning.

I plan to give in my notice by the end of July.

Although I make up my mind to quit, I still worry when I can find my next job.

 

6/18

My son got really good grades at his English final exam and he told me his grades was 97.5 marks.

However, he still made careless errors on the exam so he was worried that I would be angry at him.

I told him he was great and I didn't get angry.

 

6/19

The computers and servers of our company often crash and that always interrupts me at work.

After going home, my son told me his grades of Mandarin and Life Curriculum final exam are 97 and 95 marks.

I am really glad that he has made good progress in Mandarin and Life Curriculum.

 

6/20

Today is my birthday and the first present is an red envelope given by my husband.

When I got to my office and opened my laptop, I found the red envelope sticked on my screen.

The red envelope reads, "Happy Birthday to Derek's Mom" and it surprises me.

 

 

 


 

*workload:工作負擔

*snitch on sb:打sb的小報告

*be in low spirits:情緒低落

*review quiz:複習小考

*final exam:期末考

*review for the final exam:為了複習期末考

*make careless errors on exam:考試時常粗心錯

*get really good grades:考得很好

*one's grade is xx marks:考xx分

*complain to sb. about sth.:對sb.抱怨sth.

*comfort :安慰

*preach to sb.:對sb說教

*be bloated:脹氣

*have hiccups:打嗝

*have a stomach ulcer:胃潰瘍

*have a gastroscopy:照胃鏡

*give in one's notice:提辭呈

*make up one's mind to... :下定決心

*interrupt sb at work:打斷某人的工作

*Mandarin:國語

*Life Curriculum:生活課

*make good progress in xxxx:在xxx方面有很大的進步

*red envelope:紅包

*xxx TWD in red envelope:xxx元的紅包

*xxx reads/says, "........":xxx上面寫著....

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()