Android-приложение - GPS, Wi-Fi и Twitter

У меня есть приложение для Android для проекта в школе. Цель приложения - получить местоположение GPS, выполнить сканирование доступных сетей Wi-Fi и связать эту информацию в строку символов для твита (также приложением). Части gps и wifi написаны с нуля, а код твиттера - это материал с открытым исходным кодом, который нашел один из моих товарищей по команде. Никто из нас не имел опыта программирования под Android, поэтому мы изучали то, что нам нужно, только летать. Приложение работает прямо сейчас, но не достаточно хорошо. Обычно он может отключить некоторые твиты, но всегда в конечном итоге получает ANR. Я думаю, что это, вероятно, происходит от кода GPS или WIFI, так как это то, что мы написали сами. Мы написали отдельное приложение только с кодом твиттера, чтобы протестировать его, и это, казалось, работало нормально, так что я не думаю, что это вызывает проблему.

Могут ли некоторые программисты Android с большим опытом взглянуть на этот код и указать на любые проблемы, которые они видят, в частности, что может быть причиной того, что это приложение вызывает ANR? Также, если кто-то захочет предложить лучшую архитектуру / структуру, чем у нас в настоящее время, я был бы заинтересован. Когда приложение работает, мы получаем твит, который выглядит примерно так:

ajd7v-34 U0b0ed38fc_____________________00b0ed39c4_____________________W0b0edf50c + 44974893-093232387

это соответствующий код из нашего класса деятельности...

    private Intent in;
public static TextView textView1;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_spectral_tweets);


    mConsumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

    mProvider = new DefaultOAuthProvider(
            "http://api.twitter.com/oauth/request_token",
            "http://api.twitter.com/oauth/access_token",
            "http://api.twitter.com/oauth/authorize");

    prefs = PreferenceManager.getDefaultSharedPreferences(this);
    String token = prefs.getString("token", null);
    String tokenSecret = prefs.getString("tokenSecret", null);

    if (token != null && tokenSecret != null) {
        mConsumer.setTokenWithSecret(token, tokenSecret);
        oauthClient = new OAuthSignpostClient(CONSUMER_KEY,
                CONSUMER_SECRET, token, tokenSecret);
        twitter = new Twitter(TWITTER_USER, oauthClient);
    } else {
        Log.d(TAG, "onCreate. Not Authenticated Yet " );
        new OAuthAuthorizeTask().execute();
    }

    in = new Intent(this, BackgroundService.class);
    textView1 = (TextView) findViewById(R.id.textView1);
    changeText("On Create");
}

public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_spectral_tweets, menu);
    return true;
}

/**
 * Changes textView1 to string msg
 */
public static void changeText(String msg) {
    textView1.setText(msg);
}

/**
 * tell the service to start displaying GPS/WIFI updates
 */
public void startMessages(View view) {
    startService(in);
}

/**
 * tell the service to stop displaying GPS/WIFI updates
 */
public void stopMessages(View view) {
    changeText("stopped");
    stopService(in);
}

/**
 * When the BACK key is pressed ask the user if they want to quit
 * if they do then stop the service and exit the program
 */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
    if ((keyCode == KeyEvent.KEYCODE_BACK)) {
        @SuppressWarnings("unused")
        AlertDialog alertbox = new AlertDialog.Builder(this)
        .setMessage("Do you want to exit the application?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            // stop the service and end the program
            public void onClick(DialogInterface arg0, int arg1) {
                stopService(in);
                finish();
            }
        })
        .setNegativeButton("No", new DialogInterface.OnClickListener() {
            // return to program
            public void onClick(DialogInterface arg0, int arg1) {}
        })

        .show();
    }
    return super.onKeyDown(keyCode, event);
}

это код из нашего сервиса, который обрабатывает GPS и Wi-Fi вещи...

public class BackgroundService extends Service implements LocationListener
{
private static Timer repeater = new Timer();
private static LocationManager lm;
private getInfoAndTweet getAndTweet = this.new getInfoAndTweet();

private static final int MIN_TIME_MILLISECONDS = 0;
private static final int MIN_DIST_METERS = 0;
private static final int frequency = 30 * 1000;
private double temp_long = 0.0;
private double temp_lat = 0.0;
private int temp_count = 0;
private boolean thread_running = false;


@Override
public IBinder onBind(Intent arg0) {
    return null;
}

public void onCreate()
{
    super.onCreate();
    lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_MILLISECONDS, MIN_DIST_METERS, this);
    Toast.makeText(getApplicationContext(), "Location display is on", Toast.LENGTH_SHORT).show();
    startService();
}

public void onDestroy()
{
    repeater.cancel();
    lm.removeUpdates(this);
    unregisterReceiver(getAndTweet.receiver);
    Toast.makeText(getApplicationContext(), "Location display is off", Toast.LENGTH_SHORT).show();
    temp_lat = 0;
    temp_long = 0;
    temp_count = 0;
}

private void startService()
{
    repeater.scheduleAtFixedRate(getAndTweet, 0, frequency);
}

/* this class will contain all of the GPS and WIFI classes so that none of that stuff clogs up the main thread */
private class getInfoAndTweet extends TimerTask
{
    WifiManager wifi;
    BroadcastReceiver receiver;

    DecimalFormat lat = new DecimalFormat("00.000000");
    DecimalFormat lon = new DecimalFormat("000.000000");
    String gps_info;
    String wifi_info;
    String final_string;
    private final String hashtag = "#ajd7v-34 ";
    int count = 0;

    private class WIFIscanner extends BroadcastReceiver
    {

        private final ArrayList<Integer> channel_numbers = new ArrayList<Integer> (Arrays.asList(0, 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462));
        List <ScanResult> results;
        Map<Integer, String> levels = new HashMap<Integer, String>();
        String empty_channel = "__________";        // 10 spaces

        public WIFIscanner()
        {
            init_levels();
        }

            public void onReceive(Context context, Intent intent)
        {
            wifi_info = "";
            results = wifi.getScanResults();
            ScanResult sr;
            Iterator<ScanResult> it = results.iterator();
            ScanResult channel_info[] = new ScanResult[12];

            for (int i = 1; i < 12; i++)
            {
                channel_info[i] = null;
            }

            while (it.hasNext())
            {
                sr = it.next();
                int channel = channel_numbers.indexOf(Integer.valueOf(sr.frequency));

                if (channel_info[channel] == null)
                {
                    channel_info[channel] = sr;
                }
                else
                {
                    if (channel_info[channel].level < sr.level)
                    {
                        channel_info[channel] = sr;
                    }
                }
            }

            for (int i = 1; i < 12; i++)
            {
                if (channel_info[i] != null)
                {
                    wifi_info += (levels.get(channel_info[i].level) == null ? "0" : levels.get(channel_info[i].level))  + channel_info[i].BSSID.replace(":", "").substring(2, 11);
                }
                else
                {
                    wifi_info += empty_channel;
                }
            }

            final_string = hashtag + wifi_info + gps_info;
            if (temp_count != 0)
            {
                if(Twitter_Test_AppActivity.twitter != null) {
                    Twitter_Test_AppActivity.twitter.setStatus(final_string);
                    Twitter_Test_AppActivity.changeText("Auto Tweet Sent: " + count + "\t" + final_string);
                } else {
                    Twitter_Test_AppActivity.changeText("Tweet not sent");
                }
            }
            else
            {
                Twitter_Test_AppActivity.changeText(count + "\tno new GPS info");
            }
            thread_running = false;
        }

    }

    public void run()
    {
        thread_running = true;
        wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        if (receiver == null)
        {
            receiver = new WIFIscanner();
        }
        registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

        String latitude = lat.format(temp_lat / temp_count).replace(".",  "");
        String longitude = lon.format(temp_long / temp_count).replace(".",  "");
        gps_info = ((temp_lat / temp_count) > 0 ? "+" : "") + latitude + ((temp_long / temp_count) > 0 ? "+" : "") + longitude;

        wifi.startScan();
        count++;
    }
}

public void onLocationChanged(Location location) {
    if (thread_running)
    {
        temp_lat += location.getLatitude();
        temp_long += location.getLongitude();
        temp_count ++;
    }
}

public void onProviderDisabled(String provider) {
    Toast.makeText(getApplicationContext(), "GPS disabled", Toast.LENGTH_SHORT).show();

}

public void onProviderEnabled(String provider) {
    Toast.makeText(getApplicationContext(), "GPS enabled", Toast.LENGTH_SHORT).show();

}

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

}

РЕДАКТИРОВАТЬ, поскольку я больше думал об этом, мне пришло в голову, что код twitter, работающий в главном потоке, может также вызывать мой ANR. сеть в школе не самая лучшая, и у моего ноутбука даже иногда возникают проблемы (не по вине моего ноутбука, везде хорошо). Может ли медленное или плохое сетевое соединение вызывать зависание моего основного потока при попытке отправить твит, в результате чего телефон ANR моего приложения?

1 ответ

Решение

Прежде всего TimerTask это не лучшее место для хранения ваших данных. Используй свой TimerTask только периодически выполнять некоторые методы из вашего Service,

Во-вторых, лучше сделать все внутренние классы всех Context связанные компоненты, такие как Activity или же Service как static классы. Это защитит ваш код от утечек памяти.

Также вам лучше зарегистрироваться BroadcastRreceiver только один раз и не делайте этого при каждом вызове задачи таймера.

Я искренне рекомендую вам прочитать что-нибудь, например книгу Рето Мейера "Разработка приложений для Android".

Другие вопросы по тегам