java thread not exit_如何在Java中正确、安全地启动及关闭Thread?

当我启动或关闭Thread的时候几乎每次都会出现闪退现象,感觉就是看运气,这次只是加了个判断thread是否为null就完全呵呵了。没有系统地去学习过thread(唉,真是)。

fce13f8b7032f7dbceca2bd2c992b417.png

下面是一些相关代码:(线程是pitch_detector_thread_)

SheetPage.java

package com.fyp.flipfreely;

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.content.Context;

import android.content.Intent;

import android.media.MediaPlayer;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.view.GestureDetector;

import android.view.GestureDetector.OnGestureListener;

import android.view.Menu;

import android.view.MotionEvent;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.animation.AnimationUtils;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.LinearLayout;

import android.widget.Toast;

import android.widget.ViewFlipper;

import com.example.AndroidTuner.PitchDetector;

import com.fyp.fileIO.mXMLReader;

import com.fyp.midi.MidiDriver;

import com.fyp.midi.SimpleAnalysis;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.util.HashMap;

import java.util.Map;

import jm.music.data.Score;

/**

* A simple {@link Fragment} subclass.

*

*/

public class SheetPage extends Activity implements OnGestureListener,OnClickListener,

MidiDriver.OnMidiStartListener {

protected MidiDriver midi;

protected MediaPlayer player;

/*SimpleOnGestureListener可以用来只复写自己想要的手势,从而避免垃圾代码*/

private ViewFlipper sheetFlipper;

private GestureDetector detector;

private String title="";

//three tab, each button for each tab

private LinearLayout mTabSearch;

private LinearLayout mTabCollection;

private LinearLayout mTabSetting;

private LinearLayout mTabCollect;

private LinearLayout mTabPlay;

private Button mTabStart;

private SearchPage searchF;

private SettingPage settingF;

private CollectionPage collectionF;

private Score s=new Score();

private String directoryName="SmartSheet";

private String dirRoot = Environment.getExternalStorageDirectory().getAbsolutePath()

+ File.separator+directoryName+File.separator;

private String temptitle="";

int[] pitches=null;

int[] tmp=null;/*store the pitch from the real-time sound*/

int correctHit=0;

int counter=0;

int totalHits=0;

private static final int advance=4;/*提前四个音翻页*/

private static double accuracy=0.999;

private static int speed=50;

private static boolean auto=true;

Thread pitch_detector_thread_;

// private String[] collectionArray;//用来获取arrays中的数据之后进行数据的添加

public SheetPage() {

// Required empty public constructor

}

private void init() throws IOException {

mXMLReader xmlReader=new mXMLReader();

Map map=new HashMap();

map=xmlReader.getSystemInitInfo();

accuracy=Double.valueOf(map.get("accuracy"));

speed=Integer.valueOf(map.get("speed"));

auto=Boolean.valueOf(map.get("automation"));

}

@Override

protected void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.sheet_page);

try {

init();

} catch (IOException e) {

e.printStackTrace();

}

// Toast.makeText(SheetPage.this,"zsdd", Toast.LENGTH_SHORT).show();

Intent intent= getIntent();

Bundle b = intent.getExtras();

if(b!=null)

{

title =(String) b.get("title");

}

// 初始化控件和声明事件

mTabSearch = (LinearLayout) findViewById(R.id.searchTab);

mTabCollection = (LinearLayout) findViewById(R.id.collectionTab);

mTabSetting = (LinearLayout) findViewById(R.id.settingTab);

mTabCollect = (LinearLayout) findViewById(R.id.collectBtn);

mTabPlay = (LinearLayout) findViewById(R.id.playBtn);

mTabStart = (Button) findViewById(R.id.startBtn);

mTabSearch.setOnClickListener(this);

mTabCollection.setOnClickListener(this);

mTabSetting.setOnClickListener(this);

mTabCollect.setOnClickListener(this);

mTabPlay.setOnClickListener(this);

mTabStart.setOnClickListener(this);

if(auto){

mTabStart.setEnabled(true);

}else{

mTabStart.setEnabled(false);

}

detector = new GestureDetector(this);

sheetFlipper = (ViewFlipper) this.findViewById(R.id.sheetFlipper);

/*initialize the title of the midi file to be found*/

//TODO unfinished function here. Only two pages can be added here.

temptitle=title.toLowerCase();

temptitle=temptitle.replaceAll(" ","_");

sheetFlipper.addView(addImageView(getResourceId(temptitle,"drawable")));/*change to pages, rather than one page*/

sheetFlipper.addView(addImageView(getResourceId("finish","drawable")));

/*获取Accuracy的阈值*/

// String acc=SettingPage.defaultAccuracy;

// if(acc.equals("Low")){

// accuracy=10.0;

// }else if(acc.equals("Medium")){

// accuracy=8.0;

// }else if(acc.equals("High")){

// accuracy=5.0;

// }

try {

/*Create directory if there doesn't exist one*/

createDir(dirRoot);

String fileName=temptitle+".mid";

String filePath=dirRoot+fileName;

/*Create midi file refers to filePath*/

File midiFile=new File(filePath);

InputStream input = this.getAssets().open(fileName);//打开这个midi的stream。

/*Convert InputStream to File*/

inputstreamtofile(input,midiFile);

StringBuffer resultStringBuffer = new StringBuffer();

String lineToRead = "";

int exitValue = 0;

Process proc = Runtime.getRuntime().exec("perl midi2chord.pl -v -s120,5,5 "+fileName);

InputStream inputStream = proc.getInputStream();

BufferedReader bufferedRreader = new BufferedReader(new InputStreamReader(inputStream));

// save first line

if ((lineToRead = bufferedRreader.readLine()) != null) {

resultStringBuffer.append(lineToRead);

}

// save next lines

while ((lineToRead = bufferedRreader.readLine()) != null) {

resultStringBuffer.append("\r\n");

resultStringBuffer.append(lineToRead);

}

// Always reading STDOUT first, then STDERR, exitValue last

proc.waitFor(); // wait for reading STDOUT and STDERR over

exitValue = proc.exitValue();

System.out.print(resultStringBuffer);

/*Analysis midi file and get pitches*/

SimpleAnalysis sa;

sa=new SimpleAnalysis(filePath);

s=sa.getScore();

totalHits=sa.getPitches().length;

/*initialize the pitch array*/

pitches=new int[totalHits];

pitches=sa.getPitches();

// for (int i=0;i

// System.out.println(pitches[i]);

// }

/*initialize the tmp array*/

tmp=new int[totalHits];

pitch_detector_thread_ = new Thread(new PitchDetector(this, new Handler()));

/*get track information*/

// /*Create MidiFile*/

// MidiFile midi = new MidiFile(midiFile);

//

// // Create a new MidiProcessor:

// MidiProcessor processor = new MidiProcessor(midi);

//

// // Register for the events you're interested in:

// EventPrinter ep2 = new EventPrinter("Individual Listener");

// processor.registerEventListener(ep2, NoteOn.class);

//

// // Start the processor:

// processor.start();

} catch (Exception e) {

e.printStackTrace();

}

}

public int getResourceId(String resName,String docName){

/*get resource's id*/

Context ctx=getBaseContext();

int resId = getResources().getIdentifier(resName, docName , ctx.getPackageName());

return resId;

}

private View addImageView(int id) {

ImageView iv = new ImageView(this);

iv.setImageResource(id);

return iv;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

// TODO Auto-generated method stub

return this.detector.onTouchEvent(event);

}

@Override

public boolean onDown(MotionEvent e) {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,

float velocityY) {

if (e1.getX() - e2.getX() > 90) {

this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));

this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));

this.sheetFlipper.showNext();

return true;

} else if (e1.getX() - e2.getX() < -90) {

this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out));

this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in));

this.sheetFlipper.showPrevious();

return true;

}

return false;

}

@Override

public void onLongPress(MotionEvent e) {

// TODO Auto-generated method stub

}

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,

float distanceY) {

// TODO Auto-generated method stub

return false;

}

@Override

public void onShowPress(MotionEvent e) {

// TODO Auto-generated method stub

}

@Override

public boolean onSingleTapUp(MotionEvent e) {

// TODO Auto-generated method stub

return false;

}

@Override

public void onClick(View v) {

// Obtain FragmentManager object

FragmentManager fm = getFragmentManager();

// Start Fragment event

FragmentTransaction transaction = fm.beginTransaction();

Intent intent = new Intent();

switch (v.getId())

{

case R.id.startBtn:

//when music is on, close the music first.

if (player != null)

{

player.stop();

player.release();

player=null;

}

if(!pitch_detector_thread_.isAlive()) {

pitch_detector_thread_.start();

}else if(pitch_detector_thread_.isInterrupted()){

pitch_detector_thread_.run();

}

break;

case R.id.searchTab:

if (searchF == null)

{

searchF = new SearchPage();

}

intent.putExtra("tab", "search");

intent.setClass(this, MainActivity.class);

startActivity(intent);

finish();/*finish the sheetPage activity*/

break;

case R.id.collectionTab:

if (collectionF == null)

{

collectionF = new CollectionPage();

}

intent.putExtra("tab", "collection");

intent.setClass(this, MainActivity.class);

startActivity(intent);

finish();

break;

case R.id.settingTab:

if (settingF == null)

{

settingF = new SettingPage();

}

intent.putExtra("tab", "setting");

intent.setClass(this, MainActivity.class);

startActivity(intent);

finish();

break;

case R.id.playBtn:

if (player != null)

{

player.stop();

player.release();

player=null;

}else{

//A MediaPlayer element can play midi by using a method MediaPlayer.create(Activity,midi Id);

if(pitch_detector_thread_.isAlive()){

pitch_detector_thread_.interrupt();

}

/*play the midi file*/

player = MediaPlayer.create(this, getResourceId(temptitle,"raw"));

player.start();

//Play.midi(s);

}

break;

case R.id.collectBtn:break;

}

// transaction.addToBackStack();

// ??????

transaction.commit();

}

@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;

}

/*Real-time Thread*/

@Override

public void onStop() {

super.onStop();

//sometimes work

if(pitch_detector_thread_!=null) {

if (pitch_detector_thread_.isAlive()) {

pitch_detector_thread_.interrupt();

pitch_detector_thread_ = null;

}

}

}

/*Midi Player Stuffs*/

// On resume

@Override

protected void onResume()

{

super.onResume();

// Start midi

if (midi != null)

midi.start();

}

// On pause

@Override

protected void onPause()

{

super.onPause();

// Stop midi

if (midi != null)

midi.stop();

// Stop player

if (player != null)

player.stop();

//sometimes work

if(pitch_detector_thread_!=null) {

if (pitch_detector_thread_.isAlive()) {

pitch_detector_thread_.interrupt();

pitch_detector_thread_ = null;

}

}

}

@Override

public void onMidiStart() {

sendMidi(0xc0, 6);

}

// Send a midi message

protected void sendMidi(int m, int p)

{

byte msg[] = new byte[2];

msg[0] = (byte) m;

msg[1] = (byte) p;

midi.write(msg);

}

// Send a midi message

protected void sendMidi(int m, int n, int v)

{

byte msg[] = new byte[3];

msg[0] = (byte) m;

msg[1] = (byte) n;

msg[2] = (byte) v;

midi.write(msg);

}

public void inputstreamtofile(InputStream ins,File file) throws IOException{

OutputStream os = new FileOutputStream(file);

int bytesRead = 0;

byte[] buffer = new byte[8192];

while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {

os.write(buffer, 0, bytesRead);

}

os.close();

ins.close();

}

public static boolean createDir(String destDirName) {

File dir = new File(destDirName);

if (dir.exists()) {

// System.out.println("Create directory " + destDirName + " Failed, directory already exists");

return false;

}

if (!destDirName.endsWith(File.separator)) {

destDirName = destDirName + File.separator;

}

//??????

if (dir.mkdirs()) {

// System.out.println("Create directory " + destDirName + " Successfully");

return true;

} else {

// System.out.println("Create directory " + destDirName + " Failed");

return false;

}

}

public void ShowPitchDetectionResult(double pitch)

{

double frequency=Math.round(pitch * 10) / 10.0;

int p=0;

if(frequency>120){

p=FrequencyToPitch(frequency);

Toast.makeText(SheetPage.this,p+"", Toast.LENGTH_SHORT).show();

if(counter

tmp[counter]=p;

counter++;

}else{

double sim=0;

sim=getCosineSimilarity(pitches, tmp);

if(sim>=accuracy){

this.sheetFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));

this.sheetFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));

this.sheetFlipper.showNext();

Toast.makeText(SheetPage.this,"Finish Playing, Sim="+sim, Toast.LENGTH_SHORT).show();

}else{

Toast.makeText(SheetPage.this,"Fail to flipped, Sim="+sim, Toast.LENGTH_SHORT).show();

}

tmp=null;

tmp=new int[totalHits];

counter=0;

if(pitch_detector_thread_.isAlive()) {

pitch_detector_thread_.interrupt();

}

}

}

}

public int FrequencyToPitch(double frenquence){

int pitch=0;

pitch=(int)Math.round((69+12*Math.log(frenquence/440)/Math.log((double)2)));

return pitch;

}

/**

* 两个向量可以为任意维度,但必须保持维度相同,表示n维度中的两点,获取欧几里德距离

* @param s1

* @param s2

* @return 两点间距离

* */

public double getEuclideanDistance(int[] s1, int[] s2) {

double distance = 0;

if (s1.length == s2.length) {

for (int i = 0; i < s1.length; i++) {

double temp = Math.pow((s1[i] - s2[i]), 2);

distance += temp;

}

distance = Math.sqrt(distance);

}

return distance;

}

public double getCosineSimilarity(int[] s1,int[] s2){

double similarity=0;

double topNum=0;

double bottomA=0;

double bottomB=0;

if(s1.length==s2.length){

for(int i=0;i

topNum+=s1[i]*s2[i];

bottomA+=Math.pow(s1[i],2);

bottomB+=Math.pow(s2[i],2);

}

similarity=topNum/(Math.sqrt(bottomA)*Math.sqrt(bottomB));

}

return similarity;

}

}

PitchDetection代码:

/** Copyright (C) 2009 by Aleksey Surkov.

**

** Permission to use, copy, modify, and distribute this software and its

** documentation for any purpose and without fee is hereby granted, provided

** that the above copyright notice appear in all copies and that both that

** copyright notice and this permission notice appear in supporting

** documentation. This software is provided "as is" without express or

** implied warranty.

*/

package com.example.AndroidTuner;

import android.app.AlertDialog;

import android.media.AudioFormat;

import android.media.AudioRecord;

import android.media.MediaRecorder.AudioSource;

import android.os.Handler;

import android.util.Log;

import com.fyp.flipfreely.SheetPage;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

public class PitchDetector implements Runnable {

private static String LOG_TAG = "PitchDetector";

// Currently, only this combination of rate, encoding and channel mode

// actually works.

private final static int RATE = 8000;

private final static int CHANNEL_MODE = AudioFormat.CHANNEL_CONFIGURATION_MONO;

private final static int ENCODING = AudioFormat.ENCODING_PCM_16BIT;

private final static int BUFFER_SIZE_IN_MS = 3000;

private final static int CHUNK_SIZE_IN_SAMPLES = 4096; // = 2 ^

// CHUNK_SIZE_IN_SAMPLES_POW2

private final static int CHUNK_SIZE_IN_MS = 1000 * CHUNK_SIZE_IN_SAMPLES

/ RATE;

private final static int BUFFER_SIZE_IN_BYTES = RATE * BUFFER_SIZE_IN_MS

/ 1000 * 2;

private final static int CHUNK_SIZE_IN_BYTES = RATE * CHUNK_SIZE_IN_MS

/ 1000 * 2;

private final static int MIN_FREQUENCY = 49; // 49.0 HZ of G1 - lowest note

// for crazy Russian choir.

private final static int MAX_FREQUENCY = 1568; // 1567.98 HZ of G6 - highest

// demanded note in the

// classical repertoire

private final static int DRAW_FREQUENCY_STEP = 5;

public native void DoFFT(double[] data, int size); // an NDK library

// 'fft-jni'

public PitchDetector(SheetPage parent, Handler handler) {

parent_ = parent;

handler_ = handler;

System.loadLibrary("fft-jni");

}

private static class FreqResult {

public HashMap frequencies;

public double best_frequency;

}

public static class FrequencyCluster {

public double average_frequency = 0;

public double total_amplitude = 0;

public void add(double freq, double amplitude) {

double new_total_amp = total_amplitude + amplitude;

average_frequency = (total_amplitude * average_frequency + freq * amplitude) / new_total_amp;

total_amplitude = new_total_amp;

}

public boolean isNear(double freq) {

if (Math.abs(1 - (average_frequency / freq)) < 0.05) {

// only 5% difference

return true;

} else {

return false;

}

}

public boolean isHarmonic(double freq) {

double harmonic_factor = freq / average_frequency;

double distance_from_int = Math.abs(Math.round(harmonic_factor) - harmonic_factor);

if (distance_from_int < 0.05) {

// only 5% distance

return true;

} else {

return false;

}

}

public void addHarmony(double freq, double amp) {

total_amplitude += amp;

}

@Override public String toString() {

return "(" + average_frequency + ", " + total_amplitude + ")";

}

}

public FreqResult AnalyzeFrequencies(short[] audio_data) {

FreqResult fr = new FreqResult();

double[] data = new double[CHUNK_SIZE_IN_SAMPLES * 2];

final int min_frequency_fft = Math.round(MIN_FREQUENCY

* CHUNK_SIZE_IN_SAMPLES / RATE);

final int max_frequency_fft = Math.round(MAX_FREQUENCY

* CHUNK_SIZE_IN_SAMPLES / RATE);

for (int i = 0; i < CHUNK_SIZE_IN_SAMPLES; i++) {

data[i * 2] = audio_data[i];

data[i * 2 + 1] = 0;

}

DoFFT(data, CHUNK_SIZE_IN_SAMPLES);

double best_frequency = min_frequency_fft;

HashMap frequencies = new HashMap();

//best_frequency = min_frequency_fft;

//fr.frequencies = new HashMap();

double best_amplitude = 0;

final double draw_frequency_step = 1.0 * RATE / CHUNK_SIZE_IN_SAMPLES;

List best_frequencies = new ArrayList();

List best_amps = new ArrayList();

for (int i = min_frequency_fft; i <= max_frequency_fft; i++) {

final double current_frequency = i * 1.0 * RATE

/ CHUNK_SIZE_IN_SAMPLES;

final double draw_frequency = Math

.round((current_frequency - MIN_FREQUENCY)

/ DRAW_FREQUENCY_STEP)

* DRAW_FREQUENCY_STEP + MIN_FREQUENCY;

final double current_amplitude = Math.pow(data[i * 2], 2)

+ Math.pow(data[i * 2 + 1], 2);

final double normalized_amplitude = current_amplitude

* Math.pow(MIN_FREQUENCY * MAX_FREQUENCY, 0.5)

/ current_frequency;

Double current_sum_for_this_slot = frequencies.get(draw_frequency);

if (current_sum_for_this_slot == null) {

current_sum_for_this_slot = 0.0;

}

frequencies.put(draw_frequency, Math.pow(current_amplitude, 0.5)

/ draw_frequency_step + current_sum_for_this_slot);

if (normalized_amplitude > best_amplitude) {

best_frequency = current_frequency;

best_amplitude = normalized_amplitude;

best_frequencies.add(current_frequency);

best_amps.add(best_amplitude);

}

// test for harmonics

// e.g. 220 is a harmonic of 110, so the harmonic factor is 2.0

// and thus the decimal part is 0.0.

// 230 isn't a harmonic of 110, the harmonic_factor would be

// 2.09 and 0.09 > 0.05

//double harmonic_factor = current_frequency / best_frequency;

//if ((best_amplitude == 0) || (harmonic_factor - Math.floor(harmonic_factor) > 0.05)) {

}

List clusters = new ArrayList();

FrequencyCluster currentCluster = new FrequencyCluster();

clusters.add(currentCluster);

FrequencyCluster bestCluster = currentCluster;

if (best_frequencies.size() > 0)

{

currentCluster.add(best_frequencies.get(0), best_amps.get(0));

}

// join clusters

for(int i = 1; i < best_frequencies.size(); i++)

{

double freq = best_frequencies.get(i);

double amp = best_amps.get(i);

if (currentCluster.isNear(freq)) {

currentCluster.add(freq, amp);

continue;

}

// this isn't near, and isn't harmonic, it's a different one.

// NOTE: assuming harmonies are consecutive (no unharmonics in between harmonies)

currentCluster = new FrequencyCluster();

clusters.add(currentCluster);

currentCluster.add(freq, amp);

}

// join harmonies

FrequencyCluster nextCluster;

for(int i = 1; i < clusters.size(); i ++) {

currentCluster = clusters.get(i - 1);

nextCluster = clusters.get(i);

if (currentCluster.isHarmonic(nextCluster.average_frequency)) {

currentCluster.total_amplitude += nextCluster.total_amplitude;

}

}

best_amplitude = 0;

best_frequency = 0;

for(int i = 0; i < clusters.size(); i ++) {

FrequencyCluster clu = clusters.get(i);

if (best_amplitude < clu.total_amplitude) {

best_amplitude = clu.total_amplitude;

best_frequency = clu.average_frequency;

}

}

fr.best_frequency = best_frequency;

fr.frequencies = frequencies;

return fr;

}

public void run() {

Log.e(LOG_TAG, "starting to detect pitch");

android.os.Process

.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

recorder_ = new AudioRecord(AudioSource.MIC, RATE, CHANNEL_MODE,

ENCODING, 6144);

if (recorder_.getState() != AudioRecord.STATE_INITIALIZED) {

ShowError("Can't initialize AudioRecord");

return;

}

recorder_.startRecording();

while (!Thread.interrupted()) {

try{

short[] audio_data = new short[BUFFER_SIZE_IN_BYTES / 2];

recorder_.read(audio_data, 0, CHUNK_SIZE_IN_BYTES / 2);

FreqResult fr = AnalyzeFrequencies(audio_data);

PostToUI(fr.frequencies, fr.best_frequency);

Thread.sleep(100);//阻塞过程捕获中断异常来退出,解决了too much output to process问题

}catch(InterruptedException e){

e.printStackTrace();

break;//捕获到异常之后,执行break跳出循环。

}

}

recorder_.stop();

}

private void PostToUI(final HashMap frequencies,

final double pitch) {

handler_.post(new Runnable() {

public void run() {

/*here is how the pitch transmits to SheetPage*/

// parent_.ShowPitchDetectionResult(frequencies, pitch);

parent_.ShowPitchDetectionResult(pitch);

}

});

}

private void ShowError(final String msg) {

handler_.post(new Runnable() {

public void run() {

new AlertDialog.Builder(parent_).setTitle("GuitarTuner")

.setMessage(msg).show();

}

});

}

private SheetPage parent_;

private AudioRecord recorder_;

private Handler handler_;

}

以下是报错内容:

05-19 06:14:36.946 23613-23613/com.fyp.flipfreely E/AndroidRuntime: FATAL EXCEPTION: main

java.lang.NullPointerException

at com.fyp.flipfreely.SheetPage.onClick(SheetPage.java:373)

at android.view.View.performClick(View.java:4204)

at android.view.View$PerformClick.run(View.java:17355)

at android.os.Handler.handleCallback(Handler.java:725)

at android.os.Handler.dispatchMessage(Handler.java:92)

at android.os.Looper.loop(Looper.java:137)

at android.app.ActivityThread.main(ActivityThread.java:5041)

at java.lang.reflect.Method.invokeNative(Native Method)

at java.lang.reflect.Method.invoke(Method.java:511)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)

at dalvik.system.NativeStart.main(Native Method)


版权声明:本文为weixin_39525865原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。